Ignore:
Timestamp:
08/12/11 03:42:53 (10 months ago)
Author:
jharrop
Message:

Formatted code.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/docx4j/src/main/java/org/docx4j/model/datastorage/OpenDoPEHandler.java

    r1640 r1641  
    1313import java.util.Map; 
    1414import java.util.Set; 
    15 import java.util.UUID; 
    1615import java.util.regex.Matcher; 
    1716import java.util.regex.Pattern; 
    1817 
    19 import javax.xml.bind.JAXBException; 
    2018import javax.xml.namespace.NamespaceContext; 
    2119 
     
    2422import org.docx4j.TraversalUtil; 
    2523import org.docx4j.XmlUtils; 
    26 import org.docx4j.customXmlProperties.SchemaRefs; 
    27 import org.docx4j.customXmlProperties.SchemaRefs.SchemaRef; 
    2824import org.docx4j.jaxb.Context; 
    2925import org.docx4j.jaxb.NamespacePrefixMappings; 
     
    3228import org.docx4j.openpackaging.exceptions.Docx4JException; 
    3329import org.docx4j.openpackaging.exceptions.InvalidFormatException; 
    34 import org.docx4j.openpackaging.io.SaveToZipFile; 
    3530import org.docx4j.openpackaging.packages.WordprocessingMLPackage; 
    3631import org.docx4j.openpackaging.parts.CustomXmlDataStoragePart; 
    37 import org.docx4j.openpackaging.parts.CustomXmlDataStoragePropertiesPart; 
    3832import org.docx4j.openpackaging.parts.PartName; 
    3933import org.docx4j.openpackaging.parts.WordprocessingML.AlternativeFormatInputPart; 
     
    6660 
    6761public class OpenDoPEHandler { 
    68          
    69         private static Logger log = Logger.getLogger(OpenDoPEHandler.class);     
    70          
    71         public OpenDoPEHandler(WordprocessingMLPackage wordMLPackage) throws Docx4JException { 
     62 
     63        private static Logger log = Logger.getLogger(OpenDoPEHandler.class); 
     64 
     65        public OpenDoPEHandler(WordprocessingMLPackage wordMLPackage) 
     66                        throws Docx4JException { 
    7267 
    7368                this.wordMLPackage = wordMLPackage; 
    74                  
    75                 MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();                     
    76                 if (wordMLPackage.getMainDocumentPart().getXPathsPart()==null) { 
     69 
     70                MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); 
     71                if (wordMLPackage.getMainDocumentPart().getXPathsPart() == null) { 
    7772                        throw new Docx4JException("OpenDoPE XPaths part missing"); 
    7873                } else { 
    79                         xPaths = wordMLPackage.getMainDocumentPart().getXPathsPart().getJaxbElement(); 
    80                         log.debug( XmlUtils.marshaltoString(xPaths, true, true)); 
    81                 } 
    82                 if (wordMLPackage.getMainDocumentPart().getConditionsPart()!=null) { 
    83                         conditions = wordMLPackage.getMainDocumentPart().getConditionsPart().getJaxbElement(); 
    84                         log.debug( XmlUtils.marshaltoString(conditions, true, true)); 
    85                 } 
    86                 if (wordMLPackage.getMainDocumentPart().getComponentsPart()!=null) { 
    87                         components = wordMLPackage.getMainDocumentPart().getComponentsPart().getJaxbElement(); 
    88                         log.debug( XmlUtils.marshaltoString(components, true, true)); 
    89                 } 
    90  
    91                 shallowTraversor = new ShallowTraversor();               
    92                 shallowTraversor.wordMLPackage = wordMLPackage;  
    93         } 
    94          
     74                        xPaths = wordMLPackage.getMainDocumentPart().getXPathsPart() 
     75                                        .getJaxbElement(); 
     76                        log.debug(XmlUtils.marshaltoString(xPaths, true, true)); 
     77                } 
     78                if (wordMLPackage.getMainDocumentPart().getConditionsPart() != null) { 
     79                        conditions = wordMLPackage.getMainDocumentPart() 
     80                                        .getConditionsPart().getJaxbElement(); 
     81                        log.debug(XmlUtils.marshaltoString(conditions, true, true)); 
     82                } 
     83                if (wordMLPackage.getMainDocumentPart().getComponentsPart() != null) { 
     84                        components = wordMLPackage.getMainDocumentPart() 
     85                                        .getComponentsPart().getJaxbElement(); 
     86                        log.debug(XmlUtils.marshaltoString(components, true, true)); 
     87                } 
     88 
     89                shallowTraversor = new ShallowTraversor(); 
     90                shallowTraversor.wordMLPackage = wordMLPackage; 
     91        } 
     92 
    9593        private WordprocessingMLPackage wordMLPackage; 
    9694        private ShallowTraversor shallowTraversor; 
    97          
     95 
    9896        public final static String BINDING_ROLE_REPEAT = "od:repeat"; 
    9997        public final static String BINDING_ROLE_CONDITIONAL = "od:condition"; 
     
    102100        public final static String BINDING_ROLE_COMPONENT_BEFORE = "od:continuousBefore"; 
    103101        public final static String BINDING_ROLE_COMPONENT_AFTER = "od:continuousAfter"; 
    104          
    105          
    106         /* --------------------------------------------------------------------------- 
    107          * Pre-processing of content controls which have a tag containing "bindingrole" 
    108          *  
     102 
     103        /* 
     104         * -------------------------------------------------------------------------- 
     105         * - Pre-processing of content controls which have a tag containing 
     106         * "bindingrole" 
    109107         */ 
    110                  
    111                 private  org.opendope.conditions.Conditions conditions; 
    112                 private  org.opendope.xpaths.Xpaths xPaths; 
    113                 private  org.opendope.components.Components components; 
    114                  
    115     private  boolean removeSdtCellsOnFailedCondition; 
    116  
    117   /** 
    118    * Configure, how the preprocessor handles conditions on table cells. 
    119    *  
    120    * If set to <code>false</code>, conditional SDT cells are replaced by empty cells. This is the default behavior. 
    121    *  
    122    * If set to <code>true</code>, conditional SDT cells are removed entirely. Note that the table geometry is not 
    123    * changed; hence this works better without dynamic table widths / no global width settings. 
    124    *  
    125    * Due to the architecture of this class, this is a static flag changing the behavior of all following calls to 
    126    * {@link #preprocess}. 
    127    *  
    128    * @param removeSdtCellsOnFailedCondition The new value for the cell removal flag. 
    129    */ 
    130     public void setRemoveSdtCellsOnFailedCondition(boolean removeSdtCellsOnFailedCondition) { 
    131       this.removeSdtCellsOnFailedCondition = removeSdtCellsOnFailedCondition; 
    132     } 
    133                  
    134                 /** 
    135                  * Preprocess content controls which have tag "od:condition|od:repeat|od:component". 
    136                  *  
    137                  * It is "preprocess" in the sense that it is "pre" opening in Word 
    138                  *  
    139                  * The algorithm is as follows: 
    140                  *  
    141                  *  Inject components first.   
    142                  *  
    143                  *      Look at each top level SDT (ShallowTraversor).   
    144                  *  If it does not have a real data binding, it might have a bindingrole tag 
    145                  *  we need to process (processBindingRoleIfAny). 
    146                  *   
    147                  *  Conditionals are easy. 
    148                  *   
    149                  *  processRepeat method: 
    150                  *   
    151                  *  - clones the sdt n times 
    152                  *   
    153                  *  - invokes DeepTraversor which changes xpath binding on descendant sdts 
    154                  *    (both sdts with real bindings and sdts with bindingrole tags). 
    155                  *     
    156                  *  It is not the job of DeepTraversor to expand out any other repeats it 
    157                  *  might encounter, or to resolve conditionals. 
    158                  *   
    159                  *  Those things are done by ShallowTraversor, to which control returns, 
    160                  *  as it continues its traverse. 
    161                  *  
    162                  *  The implementation of 13 Sept 2010 replaced the previous XPath based 
    163                  *  implementation, which did not support nested repeats.  I've chosen to 
    164                  *  build this around TraversalUtil, instead of using XSLT, and this 
    165                  *  seems to have worked out nicely. 
    166                  *   
    167                  *  The implementation of 10 October 2010 replaced the v1 conventions 
    168                  *  implementation with a v2 implementation.  The main method in this 
    169                  *  class can convert v1 documents to v2. The v2 implementation is not yet 
    170                  *  complete. All v1 features are implemented, but not the new v2 
    171                  *  stuff (eg complex conditions). 
    172                  *  
    173                  * @param documentPart 
    174                  * @throws Docx4JException 
    175                  */ 
    176                 public WordprocessingMLPackage preprocess() throws Docx4JException { 
    177  
    178                         do { 
    179                                 // A component can apply in both the main document part, 
    180                                 // and in headers/footers. See further http://forums.opendope.org/Support-components-in-headers-footers-tp2964174p2964174.html  
    181                                 // A component added to the 
    182                                 // main document part could add new headers/footers. 
    183                                 // So we need to work out what parts to preprocess  
    184                                 // here inside this do loop.  
    185                                 Set<ContentAccessor> partList = getParts(wordMLPackage); 
    186                                          
    187                                 // Process repeats and conditionals. 
    188                                 for (ContentAccessor part : partList) {                                  
    189                                         new TraversalUtil(part, shallowTraversor);  
     108 
     109        private org.opendope.conditions.Conditions conditions; 
     110        private org.opendope.xpaths.Xpaths xPaths; 
     111        private org.opendope.components.Components components; 
     112 
     113        private boolean removeSdtCellsOnFailedCondition; 
     114 
     115        /** 
     116         * Configure, how the preprocessor handles conditions on table cells. 
     117         *  
     118         * If set to <code>false</code>, conditional SDT cells are replaced by empty 
     119         * cells. This is the default behavior. 
     120         *  
     121         * If set to <code>true</code>, conditional SDT cells are removed entirely. 
     122         * Note that the table geometry is not changed; hence this works better 
     123         * without dynamic table widths / no global width settings. 
     124         *  
     125         * Due to the architecture of this class, this is a static flag changing the 
     126         * behavior of all following calls to {@link #preprocess}. 
     127         *  
     128         * @param removeSdtCellsOnFailedCondition 
     129         *            The new value for the cell removal flag. 
     130         */ 
     131        public void setRemoveSdtCellsOnFailedCondition( 
     132                        boolean removeSdtCellsOnFailedCondition) { 
     133                this.removeSdtCellsOnFailedCondition = removeSdtCellsOnFailedCondition; 
     134        } 
     135 
     136        /** 
     137         * Preprocess content controls which have tag 
     138         * "od:condition|od:repeat|od:component". 
     139         *  
     140         * It is "preprocess" in the sense that it is "pre" opening in Word 
     141         *  
     142         * The algorithm is as follows: 
     143         *  
     144         * Inject components first. 
     145         *  
     146         * Look at each top level SDT (ShallowTraversor). If it does not have a real 
     147         * data binding, it might have a bindingrole tag we need to process 
     148         * (processBindingRoleIfAny). 
     149         *  
     150         * Conditionals are easy. 
     151         *  
     152         * processRepeat method: 
     153         *  
     154         * - clones the sdt n times 
     155         *  
     156         * - invokes DeepTraversor which changes xpath binding on descendant sdts 
     157         * (both sdts with real bindings and sdts with bindingrole tags). 
     158         *  
     159         * It is not the job of DeepTraversor to expand out any other repeats it 
     160         * might encounter, or to resolve conditionals. 
     161         *  
     162         * Those things are done by ShallowTraversor, to which control returns, as 
     163         * it continues its traverse. 
     164         *  
     165         * The implementation of 13 Sept 2010 replaced the previous XPath based 
     166         * implementation, which did not support nested repeats. I've chosen to 
     167         * build this around TraversalUtil, instead of using XSLT, and this seems to 
     168         * have worked out nicely. 
     169         *  
     170         * The implementation of 10 October 2010 replaced the v1 conventions 
     171         * implementation with a v2 implementation. The main method in this class 
     172         * can convert v1 documents to v2. The v2 implementation is not yet 
     173         * complete. All v1 features are implemented, but not the new v2 stuff (eg 
     174         * complex conditions). 
     175         *  
     176         * @param documentPart 
     177         * @throws Docx4JException 
     178         */ 
     179        public WordprocessingMLPackage preprocess() throws Docx4JException { 
     180 
     181                do { 
     182                        // A component can apply in both the main document part, 
     183                        // and in headers/footers. See further 
     184                        // http://forums.opendope.org/Support-components-in-headers-footers-tp2964174p2964174.html 
     185                        // A component added to the 
     186                        // main document part could add new headers/footers. 
     187                        // So we need to work out what parts to preprocess 
     188                        // here inside this do loop. 
     189                        Set<ContentAccessor> partList = getParts(wordMLPackage); 
     190 
     191                        // Process repeats and conditionals. 
     192                        for (ContentAccessor part : partList) { 
     193                                new TraversalUtil(part, shallowTraversor); 
     194                        } 
     195 
     196                        // Convert any sdt with <w:tag w:val="od:component=comp1"/> 
     197                        // to altChunk, and for MergeDocx users, to 
     198                        // real WordML. 
     199                        for (ContentAccessor part : partList) { 
     200                                wordMLPackage = fetchComponents(wordMLPackage, part); 
     201                        } 
     202 
     203                } while (justGotAComponent); 
     204                // ie repeat the whole process if you got a component 
     205 
     206                return wordMLPackage; 
     207        } 
     208 
     209        private boolean justGotAComponent = false; 
     210 
     211        private static DocxFetcher docxFetcher; 
     212 
     213        public static DocxFetcher getDocxFetcher() { 
     214                return docxFetcher; 
     215        } 
     216 
     217        public static void setDocxFetcher(DocxFetcher docxFetcher) { 
     218                OpenDoPEHandler.docxFetcher = docxFetcher; 
     219        } 
     220 
     221        private Set<ContentAccessor> getParts(WordprocessingMLPackage srcPackage) { 
     222 
     223                Set<ContentAccessor> partList = new HashSet<ContentAccessor>(); 
     224 
     225                partList.add(srcPackage.getMainDocumentPart()); 
     226 
     227                // Add headers/footers 
     228                RelationshipsPart rp = srcPackage.getMainDocumentPart() 
     229                                .getRelationshipsPart(); 
     230                for (Relationship r : rp.getRelationships().getRelationship()) { 
     231 
     232                        if (r.getType().equals(Namespaces.HEADER)) { 
     233                                partList.add((HeaderPart) rp.getPart(r)); 
     234                        } else if (r.getType().equals(Namespaces.FOOTER)) { 
     235                                partList.add((FooterPart) rp.getPart(r)); 
     236                        } 
     237                } 
     238 
     239                return partList; 
     240        } 
     241 
     242        private WordprocessingMLPackage fetchComponents( 
     243                        WordprocessingMLPackage srcPackage, ContentAccessor contentAccessor) 
     244                        throws Docx4JException { 
     245 
     246                // convert components to altChunk 
     247                Map<Integer, CTAltChunk> replacements = new HashMap<Integer, CTAltChunk>(); 
     248                Integer index = 0; 
     249                justGotAComponent = false; 
     250 
     251                LinkedList<Integer> continuousBeforeIndex = new LinkedList<Integer>(); 
     252                List<Boolean> continuousBefore = new ArrayList<Boolean>(); 
     253 
     254                List<Boolean> continuousAfter = new ArrayList<Boolean>(); 
     255 
     256                for (Object block : contentAccessor.getContent()) { 
     257 
     258                        // Object ublock = XmlUtils.unwrap(block); 
     259                        if (block instanceof org.docx4j.wml.SdtBlock) { 
     260 
     261                                org.docx4j.wml.SdtBlock sdt = (org.docx4j.wml.SdtBlock) block; 
     262 
     263                                Tag tag = getSdtPr(sdt).getTag(); 
     264 
     265                                if (tag == null) { 
     266                                        List<Object> newContent = new ArrayList<Object>(); 
     267                                        newContent.add(sdt); 
     268                                        continue; 
    190269                                } 
    191                                
    192                                 // Convert any sdt with <w:tag w:val="od:component=comp1"/> 
    193                                 // to altChunk, and for MergeDocx users, to  
    194                                 // real WordML. 
    195                                 for (ContentAccessor part : partList) { 
    196                                         wordMLPackage = fetchComponents(wordMLPackage, part); 
     270 
     271                                log.info(tag.getVal()); 
     272 
     273                                QueryString qs = new QueryString(); 
     274                                HashMap<String, String> map = qs.parseQueryString(tag.getVal(), 
     275                                                true); 
     276 
     277                                String componentId = map.get(BINDING_ROLE_COMPONENT); 
     278                                if (componentId == null) 
     279                                        continue; 
     280 
     281                                // Convert the sdt to a w:altChunk 
     282                                // .. get the IRI 
     283                                String iri = ComponentsPart.getComponentById(components, 
     284                                                componentId).getIri(); 
     285                                log.debug("Fetching " + iri); 
     286 
     287                                if (docxFetcher == null) { 
     288                                        log.error("You need a docxFetcher (and the MergeDocx extension) to fetch components"); 
     289                                        return srcPackage; 
    197290                                } 
    198                                  
    199                          
    200                         } while (justGotAComponent); 
    201                         // ie repeat the whole process if you got a component 
    202                          
    203                         return wordMLPackage; 
    204                 } 
    205                  
    206                 private boolean justGotAComponent = false; 
    207                  
    208                 private static DocxFetcher docxFetcher; 
    209                 public static DocxFetcher getDocxFetcher() { 
    210                         return docxFetcher; 
    211                 } 
    212                 public static void setDocxFetcher(DocxFetcher docxFetcher) { 
    213                         OpenDoPEHandler.docxFetcher = docxFetcher; 
    214                 } 
    215                  
    216                 private Set<ContentAccessor> getParts(WordprocessingMLPackage srcPackage) { 
    217                          
    218                         Set<ContentAccessor> partList = new HashSet<ContentAccessor>();                  
    219                          
    220                         partList.add(srcPackage.getMainDocumentPart()); 
    221                          
    222                         // Add headers/footers 
    223                         RelationshipsPart rp = srcPackage.getMainDocumentPart().getRelationshipsPart(); 
    224                         for ( Relationship r : rp.getRelationships().getRelationship()  ) { 
    225                                  
    226                                 if (r.getType().equals(Namespaces.HEADER)) { 
    227                                         partList.add((HeaderPart)rp.getPart(r));                                         
    228                                 } else if ( r.getType().equals(Namespaces.FOOTER) ) { 
    229                                         partList.add((FooterPart)rp.getPart(r)); 
    230                                 }                        
    231                         } 
    232                          
    233                         return partList; 
    234                 } 
    235  
    236                 private WordprocessingMLPackage fetchComponents(WordprocessingMLPackage srcPackage, 
    237                                 ContentAccessor contentAccessor) throws Docx4JException { 
    238                          
    239                         // convert components to altChunk 
    240                         Map<Integer, CTAltChunk> replacements = new HashMap<Integer, CTAltChunk>(); 
    241                         Integer index = 0; 
    242                         justGotAComponent = false; 
    243                          
    244                         LinkedList<Integer> continuousBeforeIndex = new LinkedList<Integer>(); 
    245                         List<Boolean> continuousBefore = new ArrayList<Boolean>(); 
    246                          
    247                         List<Boolean> continuousAfter = new ArrayList<Boolean>(); 
    248                          
    249                 for (Object block : contentAccessor.getContent() ) { 
    250                          
    251                         //Object ublock = XmlUtils.unwrap(block); 
    252                         if (block instanceof org.docx4j.wml.SdtBlock) { 
    253                                  
    254                                 org.docx4j.wml.SdtBlock sdt = (org.docx4j.wml.SdtBlock)block; 
    255                                  
    256                                 Tag tag = getSdtPr(sdt).getTag();                                                                                
    257                                  
    258                                 if (tag==null) { 
    259                                         List<Object> newContent = new ArrayList<Object>(); 
    260                                         newContent.add(sdt); 
    261                                         continue; 
    262                                 }  
    263                                  
    264                                 log.info(tag.getVal()); 
    265  
    266                                 QueryString qs = new QueryString(); 
    267                                 HashMap<String, String> map = qs.parseQueryString(tag.getVal(), true); 
    268                                  
    269                                 String componentId = map.get(BINDING_ROLE_COMPONENT); 
    270                                 if (componentId==null) continue; 
    271                                  
    272                                 // Convert the sdt to a w:altChunk 
    273                                 // .. get the IRI 
    274                                 String iri = ComponentsPart.getComponentById(components, componentId).getIri(); 
    275                                 log.debug("Fetching " + iri); 
    276                                  
    277                                         if (docxFetcher==null) { 
    278                                                 log.error("You need a docxFetcher (and the MergeDocx extension) to fetch components"); 
    279                                                 return srcPackage; 
    280                                         }  
    281                                  
    282                                 // .. create the part 
    283                                 AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart( 
    284                                                 getNewPartName("/chunk", ".docx", srcPackage.getMainDocumentPart().getRelationshipsPart()) ); 
    285                                 afiPart.setBinaryData( 
    286                                                 docxFetcher.getDocxFromIRI(iri) ); 
    287                                  
    288                                 afiPart.setContentType(new ContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")); //docx 
    289  
    290                                 Relationship altChunkRel = srcPackage.getMainDocumentPart().addTargetPart(afiPart); 
    291                                 CTAltChunk ac = Context.getWmlObjectFactory().createCTAltChunk(); 
    292                                 ac.setId(altChunkRel.getId()); 
    293  
    294                                 replacements.put(index, ac); 
    295                                  
    296                                 // This is handled in this class 
    297                                 if (map.get(BINDING_ROLE_COMPONENT_BEFORE)!=null 
    298                                                 && map.get(BINDING_ROLE_COMPONENT_BEFORE).equals("true") ) { 
    299                                         continuousBefore.add(Boolean.TRUE); 
    300                                         continuousBeforeIndex.addFirst(index); 
    301                                         log.info("ctsBefore index: " + index); 
    302                                 } else { 
    303                                         continuousBefore.add(Boolean.FALSE); 
    304                                         continuousBeforeIndex.addFirst(index); 
    305                                 } 
    306                                  
    307                                 // The following is handled in ProcessAltChunk 
    308                                 if (map.get(BINDING_ROLE_COMPONENT_AFTER)!=null 
    309                                                 && map.get(BINDING_ROLE_COMPONENT_AFTER).equals("true") ) { 
    310                                         continuousAfter.add(Boolean.TRUE); 
    311                                 } else { 
    312                                         continuousAfter.add(Boolean.TRUE); 
    313                                 } 
    314                                  
    315                                 justGotAComponent = true; 
    316                         } 
    317                         index++; 
    318                 }                        
    319                          
    320                 if (!justGotAComponent) { 
    321                         return srcPackage; 
    322                 } 
    323                  
    324                 // Now replace in list 
    325                 for(Integer key : replacements.keySet() ) { 
    326                         contentAccessor.getContent().set(key, replacements.get(key));                    
    327                 } 
    328                  
    329                 // Go through docx in reverse order  
    330                 List<Object> bodyChildren = contentAccessor.getContent();  
    331                 int i = 0; 
    332                 for (Integer indexIntoBody : continuousBeforeIndex ) { 
    333                          
    334                         if (continuousBefore.get(i) ) { 
    335                                 // Element before the w:altChunk 
    336                                 if (indexIntoBody==0) { 
    337 //                                      // Insert a sectPr right at the beginning of the docx? 
    338 //                                      // TODO check this isn't necessary 
    339 //                                      SectPr newSectPr = Context.getWmlObjectFactory().createSectPr(); 
    340 //                              SectPr.Type type = Context.getWmlObjectFactory().createSectPrType(); 
    341 //                              type.setVal("continuous"); 
    342 //                              newSectPr.setType( type ); 
    343 //                               
    344 //                              bodyChildren.add(0, newSectPr);                                  
    345                                          
    346                                 } else { 
    347                                         Object block = bodyChildren.get(indexIntoBody.intValue()-1);  
    348                                         if (block  instanceof P 
    349                                         && ((P)block).getPPr()!=null  
    350                                                         && ((P) block).getPPr().getSectPr() !=null) { 
    351                                                 makeContinuous(((P) block).getPPr().getSectPr()); 
    352                                         } else if (block  instanceof P) { 
    353                                                 // More likely 
    354                                                 PPr ppr = ((P) block).getPPr(); 
    355                                                 if (ppr==null) { 
    356                                                         ppr = Context.getWmlObjectFactory().createPPr(); 
    357                                                         ((P) block).setPPr(ppr); 
    358                                                 }  
    359                                                 SectPr newSectPr = Context.getWmlObjectFactory().createSectPr(); 
    360                                         SectPr.Type type = Context.getWmlObjectFactory().createSectPrType(); 
    361                                         type.setVal("continuous"); 
    362                                         newSectPr.setType( type ); 
    363                                                  
    364                                         ppr.setSectPr(newSectPr); 
    365                                         } else { 
    366                                                 // Equally likely - its a table or something, so add a p 
    367                                                 P newP = Context.getWmlObjectFactory().createP(); 
    368                                                 PPr ppr = Context.getWmlObjectFactory().createPPr(); 
    369                                                 newP.setPPr(ppr); 
    370                                                  
    371                                                 SectPr newSectPr = Context.getWmlObjectFactory().createSectPr(); 
    372                                         SectPr.Type type = Context.getWmlObjectFactory().createSectPrType(); 
    373                                         type.setVal("continuous"); 
    374                                         newSectPr.setType( type ); 
    375                                         ppr.setSectPr(newSectPr); 
    376                                          
    377                                         bodyChildren.add(indexIntoBody.intValue(), newP);       // add before altChunk                                   
    378                                         } 
    379                                 } 
    380                         } 
    381                         // else nothing specified, so go with normal MergeDocx behaviour 
    382                          
    383                         i++; 
    384                 } 
    385                          
    386                         // process altChunk 
    387                         try { 
    388                                 // Use reflection, so docx4j can be built 
    389                                 // by users who don't have the MergeDocx utility 
    390                                 Class<?> documentBuilder = Class.forName("com.plutext.merge.ProcessAltChunk");                   
    391                                 //Method method = documentBuilder.getMethod("merge", wmlPkgList.getClass());                     
    392                                 Method[] methods = documentBuilder.getMethods();  
    393                                 Method processMethod = null; 
    394                                 Method setEnsureContinuousMethod = null; 
    395                                 for (int j=0; j<methods.length; j++) { 
    396                                         log.debug(methods[j].getName()); 
    397                                         if (methods[j].getName().equals("process")) { 
    398                                                 processMethod = methods[j]; 
    399                                         } else if (methods[j].getName().equals("setEnsureContinuous")) { 
    400                                                 setEnsureContinuousMethod = methods[j]; 
    401                                         }  
    402                                 }                        
    403                                 if (processMethod==null 
    404                                                 || setEnsureContinuousMethod == null) throw new NoSuchMethodException(); 
    405                                 setEnsureContinuousMethod.invoke(null, continuousAfter); 
    406                                 return (WordprocessingMLPackage)processMethod.invoke(null, srcPackage); 
    407  
    408                         } catch (ClassNotFoundException e) { 
    409                                 extensionMissing(e); 
    410                         justGotAComponent = false; 
    411                                 return srcPackage; 
    412                                 //throw new Docx4JException("Problem processing w:altChunk", e); 
    413                         } catch (NoSuchMethodException e) { 
    414                                 //Degrade gracefully 
    415                                 extensionMissing(e); 
    416                         justGotAComponent = false; 
    417                                 return srcPackage; 
    418                                 //throw new Docx4JException("Problem processing w:altChunk", e); 
    419                         } catch (Exception e) { 
    420                                 throw new Docx4JException("Problem processing w:altChunk", e); 
    421                         }  
    422                 } 
    423                  
    424         public void makeContinuous(SectPr sectPr) { 
    425                  
    426                 if (sectPr==null) { 
    427                         log.warn("sectPr was null"); 
    428                         return; 
    429                 } 
    430                  
    431                 SectPr.Type type = Context.getWmlObjectFactory().createSectPrType(); 
    432                 type.setVal("continuous"); 
    433                 sectPr.setType( type ); 
    434                  
    435                 // columns, endnotes, footnotes, formprot, line numbers are OK 
    436                  
    437                 // null out certain page level section properties 
    438                 sectPr.setBidi(null); 
    439                 sectPr.setDocGrid(null); 
    440                 sectPr.setPaperSrc(null); 
    441                 sectPr.setPgBorders(null); 
    442                 sectPr.setPgMar(null); 
    443                 sectPr.setPgNumType(null); 
    444                 sectPr.setPgSz(null); 
    445                 sectPr.setPrinterSettings(null); 
    446                 sectPr.setSectPrChange(null); 
    447                 sectPr.setTitlePg(null); 
    448                 sectPr.setVAlign(null); 
    449         } 
    450                  
    451                  
    452         private PartName getNewPartName(String prefix, String suffix, RelationshipsPart rp) throws InvalidFormatException { 
    453                  
    454                 PartName proposed = null; 
    455                 int i=1; 
    456                 do { 
    457                          
    458                         if (i>1) { 
    459                                 proposed = new PartName( prefix + i + suffix); 
    460                         } else { 
    461                                 proposed = new PartName( prefix + suffix);                               
    462                         } 
    463                         i++; 
    464                          
    465                 } while (rp.getRel(proposed)!=null); 
    466                  
    467                 return proposed; 
    468                  
    469         } 
    470                  
    471                 public void extensionMissing(Exception e) { 
    472                         log.error("\n" + e.getClass().getName() + ": " + e.getMessage() + "\n"); 
    473                         log.error("* You don't appear to have the MergeDocx paid extension,"); 
    474                         log.error("* which is necessary to merge docx, or process altChunk."); 
    475                         log.error("* Purchases of this extension support the docx4j project."); 
    476                         log.error("* Please visit www.plutext.com if you want to buy it."); 
    477                 } 
    478                  
    479 //              private static void preprocessRun(WordprocessingMLPackage wordMLPackage, ContentAccessor content) throws Docx4JException { 
    480 //                       
    481 //                      log.info("\n\n Preprocess run.. \n\n"); 
    482 // 
    483 //                      MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); 
    484 //                       
    485 //                      if (wordMLPackage.getMainDocumentPart().getXPathsPart()==null) { 
    486 //                              throw new Docx4JException("OpenDoPE XPaths part missing"); 
    487 //                      } else { 
    488 //                              xPaths = wordMLPackage.getMainDocumentPart().getXPathsPart().getJaxbElement(); 
    489 //                              log.debug( XmlUtils.marshaltoString(xPaths, true, true)); 
    490 //                      } 
    491 //                      if (wordMLPackage.getMainDocumentPart().getConditionsPart()!=null) { 
    492 //                              conditions = wordMLPackage.getMainDocumentPart().getConditionsPart().getJaxbElement(); 
    493 //                              log.debug( XmlUtils.marshaltoString(conditions, true, true)); 
    494 //                      } 
    495 //                      if (wordMLPackage.getMainDocumentPart().getComponentsPart()!=null) { 
    496 //                              components = wordMLPackage.getMainDocumentPart().getComponentsPart().getJaxbElement(); 
    497 //                              log.debug( XmlUtils.marshaltoString(components, true, true)); 
    498 //                      } 
    499 // 
    500 //                      org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document)documentPart.getJaxbElement(); 
    501 //                      Body body =  wmlDocumentEl.getBody(); 
    502 //                       
    503 //                      shallowTraversor.wordMLPackage = wordMLPackage;  
    504 //                       
    505 //                      new TraversalUtil(body, shallowTraversor);  
    506 //                       
    507 //              } 
    508                  
    509                 //private static XPathsPart getXPathsPart(WordprocessingMLPackage wordMLPackage) { 
    510                 //      wordMLPackage.getParts(). 
    511                 //} 
    512                  
    513                  
    514                  
    515                 /** 
    516                  * This traversor duplicates the repeats, and removes false conditonals 
    517                  */ 
    518                 private class ShallowTraversor implements TraversalUtil.Callback { 
    519  
    520 //                      private static Logger log = Logger.getLogger(ShallowTraversor.class);            
    521                          
    522                         WordprocessingMLPackage wordMLPackage; 
    523                          
    524                         @Override 
    525                         public List<Object> apply(Object o) { 
    526                                  
    527                                 // apply processSdt to any sdt 
    528                                 // which might be a conditional|repeat 
    529  
    530                                 if (o instanceof org.docx4j.wml.SdtBlock  
    531                                                 || o instanceof org.docx4j.wml.SdtRun 
    532                                                 || o instanceof org.docx4j.wml.CTSdtRow  
    533                                                 || o instanceof org.docx4j.wml.CTSdtCell ) { 
    534                                          
    535                                         if (getSdtPr(o).getDataBinding()==null) { 
    536                                                 // a real binding attribute trumps any tag 
    537                                                 return processBindingRoleIfAny(wordMLPackage, o); 
    538                                         } 
    539                                                                          
     291 
     292                                // .. create the part 
     293                                AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart( 
     294                                                getNewPartName("/chunk", ".docx", srcPackage 
     295                                                                .getMainDocumentPart().getRelationshipsPart())); 
     296                                afiPart.setBinaryData(docxFetcher.getDocxFromIRI(iri)); 
     297 
     298                                afiPart.setContentType(new ContentType( 
     299                                                "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")); // docx 
     300 
     301                                Relationship altChunkRel = srcPackage.getMainDocumentPart() 
     302                                                .addTargetPart(afiPart); 
     303                                CTAltChunk ac = Context.getWmlObjectFactory() 
     304                                                .createCTAltChunk(); 
     305                                ac.setId(altChunkRel.getId()); 
     306 
     307                                replacements.put(index, ac); 
     308 
     309                                // This is handled in this class 
     310                                if (map.get(BINDING_ROLE_COMPONENT_BEFORE) != null 
     311                                                && map.get(BINDING_ROLE_COMPONENT_BEFORE) 
     312                                                                .equals("true")) { 
     313                                        continuousBefore.add(Boolean.TRUE); 
     314                                        continuousBeforeIndex.addFirst(index); 
     315                                        log.info("ctsBefore index: " + index); 
    540316                                } else { 
    541 //                                      log.warn("TODO: Handle " + o.getClass().getName()  + " (if that's an sdt)");                                     
     317                                        continuousBefore.add(Boolean.FALSE); 
     318                                        continuousBeforeIndex.addFirst(index); 
    542319                                } 
    543                                  
    544                                 // Otherwise just preserve the content 
    545                                 List<Object> newContent = new ArrayList<Object>(); 
    546                                 newContent.add(o); 
    547                                 return newContent; 
    548                                  
    549                         } 
    550  
    551                         @Override 
    552                         public boolean shouldTraverse(Object o) { 
    553                                  
    554                                 // we want to traverse all sdts, since an sdt which 
    555                                 // doesn't have a binding role might contain one 
    556                                 // which does 
    557                                  
    558                                 return true; 
    559                         } 
    560                          
    561                         @Override 
    562                         public List<Object> getChildren(Object o) { 
    563                                 return TraversalUtil.getChildrenImpl(o); 
    564                         } 
    565  
    566                         @Override 
    567                         public void walkJAXBElements(Object parent) { 
    568                                 // Breadth first 
    569  
    570                                 List<Object> newChildren = new ArrayList<Object>(); 
    571  
    572                                 Object parentUnwrapped = XmlUtils.unwrap(parent); 
    573                                 List children = getChildren(parentUnwrapped); 
    574                                 if (children == null) { 
    575                                         log.warn("no children: " + parentUnwrapped.getClass().getName()); 
    576                                         return; 
     320 
     321                                // The following is handled in ProcessAltChunk 
     322                                if (map.get(BINDING_ROLE_COMPONENT_AFTER) != null 
     323                                                && map.get(BINDING_ROLE_COMPONENT_AFTER).equals("true")) { 
     324                                        continuousAfter.add(Boolean.TRUE); 
    577325                                } else { 
    578                                         for (Object o : children) { 
    579  
    580                                                 newChildren.addAll( 
    581                                                                                         this.apply( 
    582                                                                                                 XmlUtils.unwrap(o))); 
    583                                                 // TODO: Post 2.7.0, review use of XmlUtils.unwrap(o) here; 
    584                                                 // it can result in MarshalException for 
    585                                                 // things which don't have @XmlRootElement. 
    586                                                 // We really need some unit tests to be confident 
    587                                                 // that making this change would be ok. 
     326                                        continuousAfter.add(Boolean.TRUE); 
     327                                } 
     328 
     329                                justGotAComponent = true; 
     330                        } 
     331                        index++; 
     332                } 
     333 
     334                if (!justGotAComponent) { 
     335                        return srcPackage; 
     336                } 
     337 
     338                // Now replace in list 
     339                for (Integer key : replacements.keySet()) { 
     340                        contentAccessor.getContent().set(key, replacements.get(key)); 
     341                } 
     342 
     343                // Go through docx in reverse order 
     344                List<Object> bodyChildren = contentAccessor.getContent(); 
     345                int i = 0; 
     346                for (Integer indexIntoBody : continuousBeforeIndex) { 
     347 
     348                        if (continuousBefore.get(i)) { 
     349                                // Element before the w:altChunk 
     350                                if (indexIntoBody == 0) { 
     351                                        // // Insert a sectPr right at the beginning of the docx? 
     352                                        // // TODO check this isn't necessary 
     353                                        // SectPr newSectPr = 
     354                                        // Context.getWmlObjectFactory().createSectPr(); 
     355                                        // SectPr.Type type = 
     356                                        // Context.getWmlObjectFactory().createSectPrType(); 
     357                                        // type.setVal("continuous"); 
     358                                        // newSectPr.setType( type ); 
     359                                        // 
     360                                        // bodyChildren.add(0, newSectPr); 
     361 
     362                                } else { 
     363                                        Object block = bodyChildren 
     364                                                        .get(indexIntoBody.intValue() - 1); 
     365                                        if (block instanceof P && ((P) block).getPPr() != null 
     366                                                        && ((P) block).getPPr().getSectPr() != null) { 
     367                                                makeContinuous(((P) block).getPPr().getSectPr()); 
     368                                        } else if (block instanceof P) { 
     369                                                // More likely 
     370                                                PPr ppr = ((P) block).getPPr(); 
     371                                                if (ppr == null) { 
     372                                                        ppr = Context.getWmlObjectFactory().createPPr(); 
     373                                                        ((P) block).setPPr(ppr); 
     374                                                } 
     375                                                SectPr newSectPr = Context.getWmlObjectFactory() 
     376                                                                .createSectPr(); 
     377                                                SectPr.Type type = Context.getWmlObjectFactory() 
     378                                                                .createSectPrType(); 
     379                                                type.setVal("continuous"); 
     380                                                newSectPr.setType(type); 
     381 
     382                                                ppr.setSectPr(newSectPr); 
     383                                        } else { 
     384                                                // Equally likely - its a table or something, so add a p 
     385                                                P newP = Context.getWmlObjectFactory().createP(); 
     386                                                PPr ppr = Context.getWmlObjectFactory().createPPr(); 
     387                                                newP.setPPr(ppr); 
     388 
     389                                                SectPr newSectPr = Context.getWmlObjectFactory() 
     390                                                                .createSectPr(); 
     391                                                SectPr.Type type = Context.getWmlObjectFactory() 
     392                                                                .createSectPrType(); 
     393                                                type.setVal("continuous"); 
     394                                                newSectPr.setType(type); 
     395                                                ppr.setSectPr(newSectPr); 
     396 
     397                                                bodyChildren.add(indexIntoBody.intValue(), newP); // add 
     398                                                                                                                                                        // before 
     399                                                                                                                                                        // altChunk 
    588400                                        } 
    589401                                } 
    590                                 // Replace list, so we'll traverse all the new sdts we've just 
    591                                 // created 
    592                                 TraversalUtil.replaceChildren(parentUnwrapped, newChildren); 
    593                                  
    594  
    595                                 children = getChildren(parentUnwrapped); 
    596                                 if (children == null) { 
    597                                         log.warn("no children: " + parentUnwrapped.getClass().getName()); 
    598                                 } else { 
    599                                         for (Object o : children) { 
    600  
    601                                                 // *** this.apply(o); 
    602  
    603                                                 if (this.shouldTraverse(o)) { 
    604                                                         walkJAXBElements(o); 
    605                                                 } 
    606  
     402                        } 
     403                        // else nothing specified, so go with normal MergeDocx behaviour 
     404 
     405                        i++; 
     406                } 
     407 
     408                // process altChunk 
     409                try { 
     410                        // Use reflection, so docx4j can be built 
     411                        // by users who don't have the MergeDocx utility 
     412                        Class<?> documentBuilder = Class 
     413                                        .forName("com.plutext.merge.ProcessAltChunk"); 
     414                        // Method method = documentBuilder.getMethod("merge", 
     415                        // wmlPkgList.getClass()); 
     416                        Method[] methods = documentBuilder.getMethods(); 
     417                        Method processMethod = null; 
     418                        Method setEnsureContinuousMethod = null; 
     419                        for (int j = 0; j < methods.length; j++) { 
     420                                log.debug(methods[j].getName()); 
     421                                if (methods[j].getName().equals("process")) { 
     422                                        processMethod = methods[j]; 
     423                                } else if (methods[j].getName().equals("setEnsureContinuous")) { 
     424                                        setEnsureContinuousMethod = methods[j]; 
     425                                } 
     426                        } 
     427                        if (processMethod == null || setEnsureContinuousMethod == null) 
     428                                throw new NoSuchMethodException(); 
     429                        setEnsureContinuousMethod.invoke(null, continuousAfter); 
     430                        return (WordprocessingMLPackage) processMethod.invoke(null, 
     431                                        srcPackage); 
     432 
     433                } catch (ClassNotFoundException e) { 
     434                        extensionMissing(e); 
     435                        justGotAComponent = false; 
     436                        return srcPackage; 
     437                        // throw new Docx4JException("Problem processing w:altChunk", e); 
     438                } catch (NoSuchMethodException e) { 
     439                        // Degrade gracefully 
     440                        extensionMissing(e); 
     441                        justGotAComponent = false; 
     442                        return srcPackage; 
     443                        // throw new Docx4JException("Problem processing w:altChunk", e); 
     444                } catch (Exception e) { 
     445                        throw new Docx4JException("Problem processing w:altChunk", e); 
     446                } 
     447        } 
     448 
     449        public void makeContinuous(SectPr sectPr) { 
     450 
     451                if (sectPr == null) { 
     452                        log.warn("sectPr was null"); 
     453                        return; 
     454                } 
     455 
     456                SectPr.Type type = Context.getWmlObjectFactory().createSectPrType(); 
     457                type.setVal("continuous"); 
     458                sectPr.setType(type); 
     459 
     460                // columns, endnotes, footnotes, formprot, line numbers are OK 
     461 
     462                // null out certain page level section properties 
     463                sectPr.setBidi(null); 
     464                sectPr.setDocGrid(null); 
     465                sectPr.setPaperSrc(null); 
     466                sectPr.setPgBorders(null); 
     467                sectPr.setPgMar(null); 
     468                sectPr.setPgNumType(null); 
     469                sectPr.setPgSz(null); 
     470                sectPr.setPrinterSettings(null); 
     471                sectPr.setSectPrChange(null); 
     472                sectPr.setTitlePg(null); 
     473                sectPr.setVAlign(null); 
     474        } 
     475 
     476        private PartName getNewPartName(String prefix, String suffix, 
     477                        RelationshipsPart rp) throws InvalidFormatException { 
     478 
     479                PartName proposed = null; 
     480                int i = 1; 
     481                do { 
     482 
     483                        if (i > 1) { 
     484                                proposed = new PartName(prefix + i + suffix); 
     485                        } else { 
     486                                proposed = new PartName(prefix + suffix); 
     487                        } 
     488                        i++; 
     489 
     490                } while (rp.getRel(proposed) != null); 
     491 
     492                return proposed; 
     493 
     494        } 
     495 
     496        public void extensionMissing(Exception e) { 
     497                log.error("\n" + e.getClass().getName() + ": " + e.getMessage() + "\n"); 
     498                log.error("* You don't appear to have the MergeDocx paid extension,"); 
     499                log.error("* which is necessary to merge docx, or process altChunk."); 
     500                log.error("* Purchases of this extension support the docx4j project."); 
     501                log.error("* Please visit www.plutext.com if you want to buy it."); 
     502        } 
     503 
     504        // private static void preprocessRun(WordprocessingMLPackage wordMLPackage, 
     505        // ContentAccessor content) throws Docx4JException { 
     506        // 
     507        // log.info("\n\n Preprocess run.. \n\n"); 
     508        // 
     509        // MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart(); 
     510        // 
     511        // if (wordMLPackage.getMainDocumentPart().getXPathsPart()==null) { 
     512        // throw new Docx4JException("OpenDoPE XPaths part missing"); 
     513        // } else { 
     514        // xPaths = 
     515        // wordMLPackage.getMainDocumentPart().getXPathsPart().getJaxbElement(); 
     516        // log.debug( XmlUtils.marshaltoString(xPaths, true, true)); 
     517        // } 
     518        // if (wordMLPackage.getMainDocumentPart().getConditionsPart()!=null) { 
     519        // conditions = 
     520        // wordMLPackage.getMainDocumentPart().getConditionsPart().getJaxbElement(); 
     521        // log.debug( XmlUtils.marshaltoString(conditions, true, true)); 
     522        // } 
     523        // if (wordMLPackage.getMainDocumentPart().getComponentsPart()!=null) { 
     524        // components = 
     525        // wordMLPackage.getMainDocumentPart().getComponentsPart().getJaxbElement(); 
     526        // log.debug( XmlUtils.marshaltoString(components, true, true)); 
     527        // } 
     528        // 
     529        // org.docx4j.wml.Document wmlDocumentEl = 
     530        // (org.docx4j.wml.Document)documentPart.getJaxbElement(); 
     531        // Body body = wmlDocumentEl.getBody(); 
     532        // 
     533        // shallowTraversor.wordMLPackage = wordMLPackage; 
     534        // 
     535        // new TraversalUtil(body, shallowTraversor); 
     536        // 
     537        // } 
     538 
     539        // private static XPathsPart getXPathsPart(WordprocessingMLPackage 
     540        // wordMLPackage) { 
     541        // wordMLPackage.getParts(). 
     542        // } 
     543 
     544        /** 
     545         * This traversor duplicates the repeats, and removes false conditonals 
     546         */ 
     547        private class ShallowTraversor implements TraversalUtil.Callback { 
     548 
     549                // private static Logger log = Logger.getLogger(ShallowTraversor.class); 
     550 
     551                WordprocessingMLPackage wordMLPackage; 
     552 
     553                @Override 
     554                public List<Object> apply(Object o) { 
     555 
     556                        // apply processSdt to any sdt 
     557                        // which might be a conditional|repeat 
     558 
     559                        if (o instanceof org.docx4j.wml.SdtBlock 
     560                                        || o instanceof org.docx4j.wml.SdtRun 
     561                                        || o instanceof org.docx4j.wml.CTSdtRow 
     562                                        || o instanceof org.docx4j.wml.CTSdtCell) { 
     563 
     564                                if (getSdtPr(o).getDataBinding() == null) { 
     565                                        // a real binding attribute trumps any tag 
     566                                        return processBindingRoleIfAny(wordMLPackage, o); 
     567                                } 
     568 
     569                        } else { 
     570                                // log.warn("TODO: Handle " + o.getClass().getName() + 
     571                                // " (if that's an sdt)"); 
     572                        } 
     573 
     574                        // Otherwise just preserve the content 
     575                        List<Object> newContent = new ArrayList<Object>(); 
     576                        newContent.add(o); 
     577                        return newContent; 
     578 
     579                } 
     580 
     581                @Override 
     582                public boolean shouldTraverse(Object o) { 
     583 
     584                        // we want to traverse all sdts, since an sdt which 
     585                        // doesn't have a binding role might contain one 
     586                        // which does 
     587 
     588                        return true; 
     589                } 
     590 
     591                @Override 
     592                public List<Object> getChildren(Object o) { 
     593                        return TraversalUtil.getChildrenImpl(o); 
     594                } 
     595 
     596                @Override 
     597                public void walkJAXBElements(Object parent) { 
     598                        // Breadth first 
     599 
     600                        List<Object> newChildren = new ArrayList<Object>(); 
     601 
     602                        Object parentUnwrapped = XmlUtils.unwrap(parent); 
     603                        List children = getChildren(parentUnwrapped); 
     604                        if (children == null) { 
     605                                log.warn("no children: " + parentUnwrapped.getClass().getName()); 
     606                                return; 
     607                        } else { 
     608                                for (Object o : children) { 
     609 
     610                                        newChildren.addAll(this.apply(XmlUtils.unwrap(o))); 
     611                                        // TODO: Post 2.7.0, review use of XmlUtils.unwrap(o) here; 
     612                                        // it can result in MarshalException for 
     613                                        // things which don't have @XmlRootElement. 
     614                                        // We really need some unit tests to be confident 
     615                                        // that making this change would be ok. 
     616                                } 
     617                        } 
     618                        // Replace list, so we'll traverse all the new sdts we've just 
     619                        // created 
     620                        TraversalUtil.replaceChildren(parentUnwrapped, newChildren); 
     621 
     622                        children = getChildren(parentUnwrapped); 
     623                        if (children == null) { 
     624                                log.warn("no children: " + parentUnwrapped.getClass().getName()); 
     625                        } else { 
     626                                for (Object o : children) { 
     627 
     628                                        // *** this.apply(o); 
     629 
     630                                        if (this.shouldTraverse(o)) { 
     631                                                walkJAXBElements(o); 
    607632                                        } 
     633 
    608634                                } 
    609635                        } 
    610                          
    611                 } 
    612                  
    613                 /** 
    614                  * This applies to any sdt which might be a conditional|repeat 
    615                  *   
    616                  * @param wordMLPackage 
    617                  * @param sdtParent 
    618                  * @param sdt 
    619                  * @param tag 
    620                  * @param sdtContent 
    621                  * @return 
    622                  */ 
    623                 private List<Object> processBindingRoleIfAny(WordprocessingMLPackage wordMLPackage, 
    624                                 Object sdt)  
    625                 { 
    626                         log.debug("Processing " + getSdtPr(sdt).getId().getVal() ); 
    627                         Tag tag = getSdtPr(sdt).getTag();                                                                                
    628                          
    629                         if (tag==null) { 
     636                } 
     637 
     638        } 
     639 
     640        /** 
     641         * This applies to any sdt which might be a conditional|repeat 
     642         *  
     643         * @param wordMLPackage 
     644         * @param sdtParent 
     645         * @param sdt 
     646         * @param tag 
     647         * @param sdtContent 
     648         * @return 
     649         */ 
     650        private List<Object> processBindingRoleIfAny( 
     651                        WordprocessingMLPackage wordMLPackage, Object sdt) { 
     652                log.debug("Processing " + getSdtPr(sdt).getId().getVal()); 
     653                Tag tag = getSdtPr(sdt).getTag(); 
     654 
     655                if (tag == null) { 
     656                        List<Object> newContent = new ArrayList<Object>(); 
     657                        newContent.add(sdt); 
     658                        return newContent; 
     659                } 
     660 
     661                log.info(tag.getVal()); 
     662 
     663                QueryString qs = new QueryString(); 
     664                HashMap<String, String> map = qs.parseQueryString(tag.getVal(), true); 
     665 
     666                String conditionId = map.get(BINDING_ROLE_CONDITIONAL); 
     667                String repeatId = map.get(BINDING_ROLE_REPEAT); 
     668                String xp = map.get(BINDING_ROLE_XPATH); 
     669                if (conditionId == null && repeatId == null && xp == null) { 
     670                        List<Object> newContent = new ArrayList<Object>(); 
     671                        newContent.add(sdt); 
     672                        return newContent; 
     673                } 
     674 
     675                Map<String, CustomXmlDataStoragePart> customXmlDataStorageParts = wordMLPackage 
     676                                .getCustomXmlDataStorageParts(); 
     677 
     678                if (conditionId != null) { 
     679 
     680                        log.info("Processing Conditional: " + tag.getVal()); 
     681 
     682                        // At present, this only handles simple conditions 
     683                        Condition c = ConditionsPart.getConditionById(conditions, 
     684                                        conditionId); 
     685                        if (c == null) { 
     686                                log.error("Missing condition " + conditionId); 
     687                        } 
     688                        org.opendope.xpaths.Xpaths.Xpath xpath = getXPathFromCondition(c); 
     689 
     690                        String val = BindingHandler.xpathGetString(wordMLPackage, 
     691                                        customXmlDataStorageParts, xpath.getDataBinding() 
     692                                                        .getStoreItemID(), xpath.getDataBinding() 
     693                                                        .getXpath(), xpath.getDataBinding() 
     694                                                        .getPrefixMappings()); 
     695 
     696                        log.info("Got value: " + val); 
     697 
     698                        if (new Boolean(val)) { 
     699                                log.debug("so keeping"); 
     700 
    630701                                List<Object> newContent = new ArrayList<Object>(); 
    631702                                newContent.add(sdt); 
    632703                                return newContent; 
    633                         }  
    634                          
    635                         log.info(tag.getVal()); 
    636  
    637                         QueryString qs = new QueryString(); 
    638                         HashMap<String, String> map = qs.parseQueryString(tag.getVal(), true); 
    639                          
    640                         String conditionId = map.get(BINDING_ROLE_CONDITIONAL); 
    641                         String repeatId = map.get(BINDING_ROLE_REPEAT); 
    642                         String xp = map.get(BINDING_ROLE_XPATH); 
    643                         if (conditionId==null 
    644                                         && repeatId==null 
    645                                         && xp==null 
    646                                         ) { 
     704 
     705                        } else { 
     706                                return eventuallyEmptyList(sdt); 
     707                        } 
     708 
     709                } else if (repeatId != null) { 
     710 
     711                        log.info("Processing Repeat: " + tag.getVal()); 
     712 
     713                        final List<Object> repeatResult = processRepeat(sdt, 
     714                                        customXmlDataStorageParts, wordMLPackage 
     715                                                        .getMainDocumentPart().getXPathsPart()); 
     716 
     717                        if (repeatResult.isEmpty()) { 
     718                                return eventuallyEmptyList(sdt); 
     719 
     720                        } else { 
     721                                return repeatResult; 
     722                        } 
     723 
     724                } else if (xp != null) { 
     725 
     726                        if (getSdtPr(sdt).getDataBinding() != null) { 
     727                                // the XPath evaluates to an element, which Word can 
     728                                // handle, so do nothing (shouldn't get here anyway though) 
    647729                                List<Object> newContent = new ArrayList<Object>(); 
    648730                                newContent.add(sdt); 
    649731                                return newContent; 
    650                         }  
    651  
    652                         Map<String, CustomXmlDataStoragePart> customXmlDataStorageParts 
    653                                 = wordMLPackage.getCustomXmlDataStorageParts();          
    654                          
    655                          
    656                         if (conditionId!=null) { 
    657  
    658                                 log.info("Processing Conditional: " + tag.getVal()); 
    659                                                          
    660                                 // At present, this only handles simple conditions 
    661                                 Condition c = ConditionsPart.getConditionById(conditions, conditionId); 
    662                                 if (c==null) { 
    663                                         log.error("Missing condition " + conditionId); 
    664                                 } 
    665                                 org.opendope.xpaths.Xpaths.Xpath xpath = getXPathFromCondition(c); 
    666                                  
    667                                 String val = BindingHandler.xpathGetString(wordMLPackage, 
    668                                                 customXmlDataStorageParts, 
    669                                                 xpath.getDataBinding().getStoreItemID(), 
    670                                                 xpath.getDataBinding().getXpath(), 
    671                                                 xpath.getDataBinding().getPrefixMappings() );                                    
    672                                  
    673                                 log.info("Got value: " + val); 
    674                                  
    675                                 if (new Boolean(val) ) { 
    676                                         log.debug("so keeping"); 
    677                                          
    678                                         List<Object> newContent = new ArrayList<Object>(); 
    679                                         newContent.add(sdt); 
    680                                         return newContent; 
    681                                          
    682         } else { 
    683                                   return eventuallyEmptyList(sdt); 
    684                                 } 
    685                                  
    686                         } else if ( repeatId!=null) { 
    687  
    688                                 log.info("Processing Repeat: " + tag.getVal()); 
    689                                  
    690                                 final List<Object> repeatResult = processRepeat(sdt, 
    691                                                 customXmlDataStorageParts, 
    692                                                 wordMLPackage.getMainDocumentPart().getXPathsPart()); 
    693                                  
    694                                 if (repeatResult.isEmpty()) { 
    695                                   return eventuallyEmptyList(sdt); 
    696                                    
    697                                 } else { 
    698                                   return repeatResult; 
    699                                 } 
    700                                  
    701                         } else if ( xp!=null) { 
    702                                  
    703                                 if (getSdtPr(sdt).getDataBinding()!=null) {                                      
    704                                         // the XPath evaluates to an element, which Word can 
    705                                         // handle, so do nothing (shouldn't get here anyway though) 
    706                                         List<Object> newContent = new ArrayList<Object>(); 
    707                                         newContent.add(sdt); 
    708                                         return newContent;                                       
    709                                 } 
    710                                 log.info("Processing XPath expression: " + tag.getVal()); 
    711                                 log.info(XmlUtils.marshaltoString(sdt, true, true)); 
    712                                  
    713                                 List<Object> contentList = null; 
    714                                 if (sdt instanceof org.docx4j.wml.SdtBlock) { 
    715                                         contentList =  ((org.docx4j.wml.SdtBlock) sdt).getSdtContent().getContent(); 
    716                                 } else if (sdt instanceof org.docx4j.wml.SdtRun) { // sdt in paragraph 
    717                                         contentList =  ((org.docx4j.wml.SdtRun) sdt).getSdtContent().getContent(); 
    718                                 }  
    719                                 // An CTSdtRow or CTSdtCell shouldn't be bound 
    720                                 if (contentList==null || contentList.size()==0 ) { 
    721                                         List<Object> newContent = new ArrayList<Object>(); 
    722                                         newContent.add(sdt); 
    723                                         return newContent;                                                                               
    724                                 } 
    725                                  
    726                                 // Word can't handle an XPath that returns something else 
    727                                 // eg string or boolean or number, so work this out. 
    728                                 // In principal, we could do this in this pre-processing step, 
    729                                 // or via bind.xslt.  But probably slightly better to do it here. 
    730                                 org.opendope.xpaths.Xpaths.Xpath xpathObj = XPathsPart.getXPathById(xPaths, xp); 
    731                                 String value = BindingHandler.xpathGetString( 
    732                                                                                                 wordMLPackage, 
    733                                                                                                 customXmlDataStorageParts,  
    734                                                                                                 xpathObj.getDataBinding().getStoreItemID(), 
    735                                                                                                 xpathObj.getDataBinding().getXpath(), 
    736                                                                                                 xpathObj.getDataBinding().getPrefixMappings() );                                         
    737                                 log.info(xpathObj.getDataBinding().getXpath()); 
    738                                  
    739                                 // Now insert 
    740                                 R r = null; 
    741                                 Object firstBlock = contentList.get(0); 
    742                                 if (firstBlock instanceof P ) { 
    743                                         if (((P)firstBlock).getParagraphContent().get(0) instanceof R) { 
    744                                                 r = (R)((P)firstBlock).getParagraphContent().get(0);                                             
    745                                         } 
    746                                 } else if (firstBlock instanceof R ) { 
    747                                                 r = ((R)firstBlock); 
    748                                 } 
    749                                 if (r==null) { 
    750                                         // Give up 
    751                                         log.warn("Couldn't find a run in which to insert xpath value"); 
    752                                         List<Object> newContent = new ArrayList<Object>(); 
    753                                         newContent.add(sdt); 
    754                                         return newContent;                                                                                                                       
    755                                 } 
    756                                 Text  wt = null; 
    757                                 Object firstInline = XmlUtils.unwrap(r.getRunContent().get(0)); 
    758                                 if (firstInline instanceof Text) { 
    759                                         wt = (Text)firstInline; 
    760                                 } else { 
    761                                         log.warn("First was " + firstInline ); 
    762                                         wt = Context.getWmlObjectFactory().createText(); 
    763                                         r.getRunContent().add(wt); 
    764                                 } 
    765                                 wt.setValue(value); 
    766                                 // Return the sdt with this value set 
     732                        } 
     733                        log.info("Processing XPath expression: " + tag.getVal()); 
     734                        log.info(XmlUtils.marshaltoString(sdt, true, true)); 
     735 
     736                        List<Object> contentList = null; 
     737                        if (sdt instanceof org.docx4j.wml.SdtBlock) { 
     738                                contentList = ((org.docx4j.wml.SdtBlock) sdt).getSdtContent() 
     739                                                .getContent(); 
     740                        } else if (sdt instanceof org.docx4j.wml.SdtRun) { // sdt in 
     741                                                                                                                                // paragraph 
     742                                contentList = ((org.docx4j.wml.SdtRun) sdt).getSdtContent() 
     743                                                .getContent(); 
     744                        } 
     745                        // An CTSdtRow or CTSdtCell shouldn't be bound 
     746                        if (contentList == null || contentList.size() == 0) { 
    767747                                List<Object> newContent = new ArrayList<Object>(); 
    768748                                newContent.add(sdt); 
    769                                 return newContent;                                       
    770                         }        
    771                         // shouldn't happen 
    772                         return null; 
    773                 } 
    774                  
    775   /** 
    776    * Under normal instances, return an empty list in order to remove content. 
    777    *  
    778    * If, however, this would produce a table cell without a content, add an empty content node to this table cell. 
    779    *  
    780    * For backward reasons, also replace a cell to be removed upon a condition or due to a zero item repeat with an empty 
    781    * cell according to this condition, unless the global flag {@link #removeSdtCellsOnFailedCondition} is set. 
    782    *  
    783    * @param sdt The SDT node currently being processed. 
    784    * @return The "eventually empty" node list to replace the content of <code>sdt</code>. 
    785    */ 
    786   private List<Object> eventuallyEmptyList(final Object sdt) { 
    787  
    788     final boolean sdtIsCell = sdt instanceof CTSdtCell; 
    789  
    790     final Object parent = obtainParent(sdt); 
    791     final int contentChildCount = countContentChildren(parent); 
    792     final boolean sdtIsSingleCellChild = parent instanceof Tc && contentChildCount == 1; 
    793  
    794     final List<Object> newContent; 
    795  
    796     if (sdtIsCell && !removeSdtCellsOnFailedCondition) { 
    797  
    798       final CTSdtContentCell sdtCellContent = (CTSdtContentCell) ((org.docx4j.wml.CTSdtCell) sdt).getSdtContent(); 
    799       final Tc tc = (Tc) XmlUtils.unwrap(sdtCellContent.getContent().get(0)); 
    800       tc.getContent().clear(); 
    801       final P p = Context.getWmlObjectFactory().createP(); 
    802       tc.getContent().add(p); 
    803       newContent = Arrays.asList((Object) tc); 
    804  
    805     } else if (sdtIsSingleCellChild) { 
    806  
    807       final Object p = Context.getWmlObjectFactory().createP(); 
    808       newContent = Arrays.asList(p); 
    809  
    810     } else { 
    811       newContent = Arrays.asList(); 
    812     } 
    813  
    814     return newContent; 
    815   } 
    816                  
    817     private Object obtainParent(Object sdt) { 
    818       if (! (sdt instanceof Child ))  
    819         throw new IllegalArgumentException("Object of class "+ sdt.getClass().getName() + " is not a Child"); 
    820  
    821       return ((Child) sdt).getParent(); 
    822     } 
    823      
    824     private int countContentChildren(final Object tc) { 
    825       final List<Object> selfAndSiblings = obtainChildren(tc); 
    826       int contentChildCount = 0; 
    827       for (final Object child: selfAndSiblings) 
    828         if (! (child instanceof TcPr)) 
    829           contentChildCount ++; 
    830       return contentChildCount; 
    831     } 
    832      
    833     private List<Object> obtainChildren(Object element) { 
    834       if (element instanceof ContentAccessor ) 
    835         return ((ContentAccessor) element).getContent(); 
    836        
    837       log.warn("Don't know how to access the children of " + element.getClass().getName()); 
    838       return Collections.emptyList(); 
    839     } 
    840  
    841                 public org.opendope.xpaths.Xpaths.Xpath getXPathFromCondition(Condition c) { 
    842                          
    843                         org.opendope.conditions.Xpathref xpathRef = c.getXpathref(); 
    844                          
    845                         if (xpathRef==null) { 
    846                                 log.error("Condition " + c.getId() + " references a missing xpath!" ); 
    847                         } 
    848                          
    849                         // Now get the xpath 
    850                         return XPathsPart.getXPathById(xPaths, xpathRef.getId());                                        
    851                 } 
    852                  
    853                 private List<Object>  processRepeat(Object sdt, 
    854                                 Map<String, CustomXmlDataStoragePart> customXmlDataStorageParts, 
    855                                 XPathsPart xPathsPart) { 
    856                          
    857                         Tag tag = getSdtPr(sdt).getTag();                                                                                
    858                          
    859                         HashMap<String, String> map = QueryString.parseQueryString(tag.getVal(), true); 
    860                          
    861                         String repeatId = map.get(BINDING_ROLE_REPEAT); 
    862                          
    863                         // Check, whether we are in an old repeat case. These can be removed. 
    864                         if (StringUtils.isEmpty(repeatId)) 
    865                             return new ArrayList<Object>(); 
    866  
    867                         org.opendope.xpaths.Xpaths.Xpath xpathObj = XPathsPart.getXPathById(xPaths, repeatId); 
    868  
    869                         String storeItemId = xpathObj.getDataBinding().getStoreItemID(); 
    870                         String xpath = xpathObj.getDataBinding().getXpath(); 
    871                         String prefixMappings = xpathObj.getDataBinding().getPrefixMappings(); 
    872                          
    873       // Get the bound XML   
    874       String xpathBase; 
    875 //      if (xpath.endsWith("/*")) { 
    876 //        xpathBase = xpath.substring(0, xpath.length()-2); 
    877 //      } else 
    878         if (xpath.endsWith("/")) { 
    879         xpathBase = xpath.substring(0, xpath.length()-1); 
    880    
    881         // Check, whether the xpath ends with a [1]. If so, guess it comes from a round-tripped path and strip it 
    882       } else if (xpath.endsWith("[1]")) { 
    883         xpathBase = xpath.substring(0, xpath.length() - 3); 
    884          
    885       } else { 
    886         xpathBase = xpath; 
    887       } 
    888                          
    889         // DON'T Drop any trailing position! That breaks nested repeats  
    890 //                      if (xpathBase.endsWith("]"))  
    891 //                              xpathBase = xpathBase.substring(0, xpathBase.lastIndexOf("[")); 
    892                          
    893                         log.info("/n/n Repeat: using xpath: " + xpathBase); 
    894                         NamespaceContext nsContext = new NamespacePrefixMappings();                      
    895                         List<Node> repeatedSiblings = xpathGetNodes(customXmlDataStorageParts, 
    896                                         storeItemId, xpathBase, prefixMappings);         
    897 //                                      storeItemId, xpathBase+"/*", prefixMappings);    
    898                          
    899                         // Count siblings 
    900                         int numRepeats = repeatedSiblings.size(); 
    901                         log.debug("yields REPEATS: " + numRepeats ); 
    902                          
    903                         if (numRepeats==0) { 
    904                                 return new ArrayList<Object>();  // effectively, delete 
    905                         } 
    906                          
    907                         // duplicate content here ... 
    908                         List<Object> repeated = cloneRepeatSdt( sdt, 
    909                                          xpathBase,  numRepeats); 
    910                          
    911                         // deep traverse to fix binding 
    912                         DeepTraversor dt = new DeepTraversor(); 
    913                         dt.xpathBase = xpathBase;  
    914                         for (int i=0; i<repeated.size(); i++) { 
    915                                  
    916                                 log.info("\n Traversing clone " + i); 
    917                                  
    918                                 dt.index = i;                    
    919                                 new TraversalUtil(repeated.get(i), dt);                          
    920                         } 
    921                         log.info(".. deep traversals done " ); 
    922                                          
    923                         return repeated;                         
    924                 } 
    925                  
    926                  
     749                                return newContent; 
     750                        } 
     751 
     752                        // Word can't handle an XPath that returns something else 
     753                        // eg string or boolean or number, so work this out. 
     754                        // In principal, we could do this in this pre-processing step, 
     755                        // or via bind.xslt. But probably slightly better to do it here. 
     756                        org.opendope.xpaths.Xpaths.Xpath xpathObj = XPathsPart 
     757                                        .getXPathById(xPaths, xp); 
     758                        String value = BindingHandler.xpathGetString(wordMLPackage, 
     759                                        customXmlDataStorageParts, xpathObj.getDataBinding() 
     760                                                        .getStoreItemID(), xpathObj.getDataBinding() 
     761                                                        .getXpath(), xpathObj.getDataBinding() 
     762                                                        .getPrefixMappings()); 
     763                        log.info(xpathObj.getDataBinding().getXpath()); 
     764 
     765                        // Now insert 
     766                        R r = null; 
     767                        Object firstBlock = contentList.get(0); 
     768                        if (firstBlock instanceof P) { 
     769                                if (((P) firstBlock).getParagraphContent().get(0) instanceof R) { 
     770                                        r = (R) ((P) firstBlock).getParagraphContent().get(0); 
     771                                } 
     772                        } else if (firstBlock instanceof R) { 
     773                                r = ((R) firstBlock); 
     774                        } 
     775                        if (r == null) { 
     776                                // Give up 
     777                                log.warn("Couldn't find a run in which to insert xpath value"); 
     778                                List<Object> newContent = new ArrayList<Object>(); 
     779                                newContent.add(sdt); 
     780                                return newContent; 
     781                        } 
     782                        Text wt = null; 
     783                        Object firstInline = XmlUtils.unwrap(r.getRunContent().get(0)); 
     784                        if (firstInline instanceof Text) { 
     785                                wt = (Text) firstInline; 
     786                        } else { 
     787                                log.warn("First was " + firstInline); 
     788                                wt = Context.getWmlObjectFactory().createText(); 
     789                                r.getRunContent().add(wt); 
     790                        } 
     791                        wt.setValue(value); 
     792                        // Return the sdt with this value set 
     793                        List<Object> newContent = new ArrayList<Object>(); 
     794                        newContent.add(sdt); 
     795                        return newContent; 
     796                } 
     797                // shouldn't happen 
     798                return null; 
     799        } 
     800 
     801        /** 
     802         * Under normal instances, return an empty list in order to remove content. 
     803         *  
     804         * If, however, this would produce a table cell without a content, add an 
     805         * empty content node to this table cell. 
     806         *  
     807         * For backward reasons, also replace a cell to be removed upon a condition 
     808         * or due to a zero item repeat with an empty cell according to this 
     809         * condition, unless the global flag 
     810         * {@link #removeSdtCellsOnFailedCondition} is set. 
     811         *  
     812         * @param sdt 
     813         *            The SDT node currently being processed. 
     814         * @return The "eventually empty" node list to replace the content of 
     815         *         <code>sdt</code>. 
     816         */ 
     817        private List<Object> eventuallyEmptyList(final Object sdt) { 
     818 
     819                final boolean sdtIsCell = sdt instanceof CTSdtCell; 
     820 
     821                final Object parent = obtainParent(sdt); 
     822                final int contentChildCount = countContentChildren(parent); 
     823                final boolean sdtIsSingleCellChild = parent instanceof Tc 
     824                                && contentChildCount == 1; 
     825 
     826                final List<Object> newContent; 
     827 
     828                if (sdtIsCell && !removeSdtCellsOnFailedCondition) { 
     829 
     830                        final CTSdtContentCell sdtCellContent = (CTSdtContentCell) ((org.docx4j.wml.CTSdtCell) sdt) 
     831                                        .getSdtContent(); 
     832                        final Tc tc = (Tc) XmlUtils.unwrap(sdtCellContent.getContent().get( 
     833                                        0)); 
     834                        tc.getContent().clear(); 
     835                        final P p = Context.getWmlObjectFactory().createP(); 
     836                        tc.getContent().add(p); 
     837                        newContent = Arrays.asList((Object) tc); 
     838 
     839                } else if (sdtIsSingleCellChild) { 
     840 
     841                        final Object p = Context.getWmlObjectFactory().createP(); 
     842                        newContent = Arrays.asList(p); 
     843 
     844                } else { 
     845                        newContent = Arrays.asList(); 
     846                } 
     847 
     848                return newContent; 
     849        } 
     850 
     851        private Object obtainParent(Object sdt) { 
     852                if (!(sdt instanceof Child)) 
     853                        throw new IllegalArgumentException("Object of class " 
     854                                        + sdt.getClass().getName() + " is not a Child"); 
     855 
     856                return ((Child) sdt).getParent(); 
     857        } 
     858 
     859        private int countContentChildren(final Object tc) { 
     860                final List<Object> selfAndSiblings = obtainChildren(tc); 
     861                int contentChildCount = 0; 
     862                for (final Object child : selfAndSiblings) 
     863                        if (!(child instanceof TcPr)) 
     864                                contentChildCount++; 
     865                return contentChildCount; 
     866        } 
     867 
     868        private List<Object> obtainChildren(Object element) { 
     869                if (element instanceof ContentAccessor) 
     870                        return ((ContentAccessor) element).getContent(); 
     871 
     872                log.warn("Don't know how to access the children of " 
     873                                + element.getClass().getName()); 
     874                return Collections.emptyList(); 
     875        } 
     876 
     877        public org.opendope.xpaths.Xpaths.Xpath getXPathFromCondition(Condition c) { 
     878 
     879                org.opendope.conditions.Xpathref xpathRef = c.getXpathref(); 
     880 
     881                if (xpathRef == null) { 
     882                        log.error("Condition " + c.getId() + " references a missing xpath!"); 
     883                } 
     884 
     885                // Now get the xpath 
     886                return XPathsPart.getXPathById(xPaths, xpathRef.getId()); 
     887        } 
     888 
     889        private List<Object> processRepeat(Object sdt, 
     890                        Map<String, CustomXmlDataStoragePart> customXmlDataStorageParts, 
     891                        XPathsPart xPathsPart) { 
     892 
     893                Tag tag = getSdtPr(sdt).getTag(); 
     894 
     895                HashMap<String, String> map = QueryString.parseQueryString( 
     896                                tag.getVal(), true); 
     897 
     898                String repeatId = map.get(BINDING_ROLE_REPEAT); 
     899 
     900                // Check, whether we are in an old repeat case. These can be removed. 
     901                if (StringUtils.isEmpty(repeatId)) 
     902                        return new ArrayList<Object>(); 
     903 
     904                org.opendope.xpaths.Xpaths.Xpath xpathObj = XPathsPart.getXPathById( 
     905                                xPaths, repeatId); 
     906 
     907                String storeItemId = xpathObj.getDataBinding().getStoreItemID(); 
     908                String xpath = xpathObj.getDataBinding().getXpath(); 
     909                String prefixMappings = xpathObj.getDataBinding().getPrefixMappings(); 
     910 
     911                // Get the bound XML 
     912                String xpathBase; 
     913                // if (xpath.endsWith("/*")) { 
     914                // xpathBase = xpath.substring(0, xpath.length()-2); 
     915                // } else 
     916                if (xpath.endsWith("/")) { 
     917                        xpathBase = xpath.substring(0, xpath.length() - 1); 
     918 
     919                        // Check, whether the xpath ends with a [1]. If so, guess it comes 
     920                        // from a round-tripped path and strip it 
     921                } else if (xpath.endsWith("[1]")) { 
     922                        xpathBase = xpath.substring(0, xpath.length() - 3); 
     923 
     924                } else { 
     925                        xpathBase = xpath; 
     926                } 
     927 
     928                // DON'T Drop any trailing position! That breaks nested repeats 
     929                // if (xpathBase.endsWith("]")) 
     930                // xpathBase = xpathBase.substring(0, xpathBase.lastIndexOf("[")); 
     931 
     932                log.info("/n/n Repeat: using xpath: " + xpathBase); 
     933                NamespaceContext nsContext = new NamespacePrefixMappings(); 
     934                List<Node> repeatedSiblings = xpathGetNodes(customXmlDataStorageParts, 
     935                                storeItemId, xpathBase, prefixMappings); 
     936                // storeItemId, xpathBase+"/*", prefixMappings); 
     937 
     938                // Count siblings 
     939                int numRepeats = repeatedSiblings.size(); 
     940                log.debug("yields REPEATS: " + numRepeats); 
     941 
     942                if (numRepeats == 0) { 
     943                        return new ArrayList<Object>(); // effectively, delete 
     944                } 
     945 
     946                // duplicate content here ... 
     947                List<Object> repeated = cloneRepeatSdt(sdt, xpathBase, numRepeats); 
     948 
     949                // deep traverse to fix binding 
     950                DeepTraversor dt = new DeepTraversor(); 
     951                dt.xpathBase = xpathBase; 
     952                for (int i = 0; i < repeated.size(); i++) { 
     953 
     954                        log.info("\n Traversing clone " + i); 
     955 
     956                        dt.index = i; 
     957                        new TraversalUtil(repeated.get(i), dt); 
     958                } 
     959                log.info(".. deep traversals done "); 
     960 
     961                return repeated; 
     962        } 
     963 
    927964        private List<Object> cloneRepeatSdt(Object sdt, String xpathBase, 
    928965                        int numRepeats) { 
     
    9651002                return newContent; 
    9661003        } 
    967                  
     1004 
    9681005        private void emptyRepeatTagValue(final Tag tag) { 
    9691006 
     
    9821019                tag.setVal(emptyRepeatValue); 
    9831020        } 
    984                  
    985                 class DeepTraversor implements TraversalUtil.Callback { 
    986  
    987 //                      private static Logger log = Logger.getLogger(DeepTraversor.class); 
    988  
    989                         int index = 0; 
    990                         String xpathBase = null; 
    991  
    992                         @Override 
    993                         public List<Object> apply(Object o) { 
    994  
    995 //                              log.debug("apply for " + o.getClass().getName()); 
    996  
    997                                 if (o instanceof org.docx4j.wml.SdtBlock 
    998                                                 || o instanceof org.docx4j.wml.SdtRun 
    999                                                 || o instanceof org.docx4j.wml.CTSdtRow 
    1000                                                 || o instanceof org.docx4j.wml.CTSdtCell) { // SdtCell as well, here 
    1001  
    1002                                         processDescendantBindings(o, xpathBase, index); 
    1003                                                 // whether it has a databinding or not 
    1004  
    1005                                 } else { 
    1006 //                                      log.warn("TODO: Handle " + o.getClass().getName() 
    1007 //                                                      + " (if that's an sdt)"); 
     1021 
     1022        class DeepTraversor implements TraversalUtil.Callback { 
     1023 
     1024                // private static Logger log = Logger.getLogger(DeepTraversor.class); 
     1025 
     1026                int index = 0; 
     1027                String xpathBase = null; 
     1028 
     1029                @Override 
     1030                public List<Object> apply(Object o) { 
     1031 
     1032                        // log.debug("apply for " + o.getClass().getName()); 
     1033 
     1034                        if (o instanceof org.docx4j.wml.SdtBlock 
     1035                                        || o instanceof org.docx4j.wml.SdtRun 
     1036                                        || o instanceof org.docx4j.wml.CTSdtRow 
     1037                                        || o instanceof org.docx4j.wml.CTSdtCell) { // SdtCell as 
     1038                                                                                                                                // well, here 
     1039 
     1040                                processDescendantBindings(o, xpathBase, index); 
     1041                                // whether it has a databinding or not 
     1042 
     1043                        } else { 
     1044                                // log.warn("TODO: Handle " + o.getClass().getName() 
     1045                                // + " (if that's an sdt)"); 
     1046                        } 
     1047 
     1048                        return null; // doesn't matter in this implementation 
     1049                } 
     1050 
     1051                @Override 
     1052                public void walkJAXBElements(Object parent) { 
     1053 
     1054                        List children = getChildren(parent); 
     1055                        if (children != null) { 
     1056 
     1057                                for (Object o : children) { 
     1058 
     1059                                        o = XmlUtils.unwrap(o); 
     1060                                        this.apply(o); 
     1061 
     1062                                        if (this.shouldTraverse(o)) { 
     1063                                                walkJAXBElements(o); 
     1064                                        } 
     1065 
    10081066                                } 
    1009  
    1010                                 return null; // doesn't matter in this implementation 
    1011                         } 
    1012  
    1013                         @Override 
    1014                         public void walkJAXBElements(Object parent) { 
    1015  
    1016                                 List children = getChildren(parent); 
    1017                                 if (children != null) { 
    1018  
    1019                                         for (Object o : children) { 
    1020  
    1021                                                 o = XmlUtils.unwrap(o); 
    1022                                                 this.apply(o); 
    1023  
    1024                                                 if (this.shouldTraverse(o)) { 
    1025                                                         walkJAXBElements(o); 
    1026                                                 } 
    1027  
    1028                                         } 
     1067                        } 
     1068                } 
     1069 
     1070                @Override 
     1071                public List<Object> getChildren(Object o) { 
     1072                        // log.debug("getChildren for " + o.getClass().getName()); 
     1073                        return TraversalUtil.getChildrenImpl(o); 
     1074                } 
     1075 
     1076                @Override 
     1077                public boolean shouldTraverse(Object o) { 
     1078                        return true; 
     1079                } 
     1080 
     1081        } 
     1082 
     1083        private void processDescendantBindings(Object sdt, String xpathBase, 
     1084                        int index) { 
     1085 
     1086                SdtPr sdtPr = getSdtPr(sdt); 
     1087 
     1088                // log.debug(XmlUtils.marshaltoString(sdtPr, true, true)); 
     1089 
     1090                // Give it a unique ID (supersedes above?) 
     1091                sdtPr.setId(); 
     1092 
     1093                // log.debug(XmlUtils.marshaltoString(sdtPr, true, true)); 
     1094                CTDataBinding binding = (CTDataBinding) XmlUtils.unwrap(sdtPr 
     1095                                .getDataBinding()); 
     1096 
     1097                String thisXPath = null; 
     1098 
     1099                // It'll have one of these three... 
     1100                String conditionId = null; 
     1101                String repeatId = null; 
     1102                String bindingId = null; 
     1103 
     1104                Condition c = null; 
     1105                org.opendope.xpaths.Xpaths.Xpath xpathObj = null; 
     1106 
     1107                Tag tag = sdtPr.getTag(); 
     1108                if (tag == null) 
     1109                        return; 
     1110                HashMap<String, String> map = QueryString.parseQueryString( 
     1111                                tag.getVal(), true); 
     1112 
     1113                if (binding == null) { 
     1114 
     1115                        conditionId = map.get(BINDING_ROLE_CONDITIONAL); 
     1116                        repeatId = map.get(BINDING_ROLE_REPEAT); 
     1117 
     1118                        if (conditionId != null) { 
     1119 
     1120                                c = ConditionsPart.getConditionById(conditions, conditionId); 
     1121                                if (c == null) { 
     1122                                        log.error("Missing condition " + conditionId); 
    10291123                                } 
    1030                         } 
    1031  
    1032                         @Override 
    1033                         public List<Object> getChildren(Object o) { 
    1034 //                              log.debug("getChildren for " + o.getClass().getName()); 
    1035                                 return TraversalUtil.getChildrenImpl(o); 
    1036                         } 
    1037  
    1038                         @Override 
    1039                         public boolean shouldTraverse(Object o) { 
    1040                                 return true; 
    1041                         } 
    1042  
    1043                 } 
    1044                  
    1045                 private void processDescendantBindings(Object sdt, 
    1046                                 String xpathBase, int index) { 
    1047                          
    1048                         SdtPr sdtPr =  getSdtPr(sdt);  
    1049                          
    1050                         //log.debug(XmlUtils.marshaltoString(sdtPr, true, true)); 
    1051                          
    1052                         // Give it a unique ID (supersedes above?) 
    1053                         sdtPr.setId(); 
    1054                          
    1055                         //log.debug(XmlUtils.marshaltoString(sdtPr, true, true)); 
    1056                         CTDataBinding binding = (CTDataBinding)XmlUtils.unwrap(sdtPr.getDataBinding()); 
    1057                          
    1058                         String thisXPath = null; 
    1059                          
    1060                         // It'll have one of these three... 
    1061                         String conditionId =null; 
    1062                         String repeatId = null; 
    1063                         String bindingId = null; 
    1064                          
    1065                         Condition c = null; 
    1066                         org.opendope.xpaths.Xpaths.Xpath xpathObj = null; 
    1067  
    1068                         Tag tag = sdtPr.getTag();        
    1069                         if (tag==null) return;                   
    1070                         HashMap<String, String> map = QueryString.parseQueryString(tag.getVal(), true); 
    1071                          
    1072                         if (binding==null) { 
    1073                                  
    1074                                 conditionId = map.get(BINDING_ROLE_CONDITIONAL); 
    1075                                 repeatId = map.get(BINDING_ROLE_REPEAT); 
    1076                                  
    1077                                 if (conditionId!=null) { 
    1078                                          
    1079                                         c = ConditionsPart.getConditionById(conditions, conditionId); 
    1080                                         if (c==null) { 
    1081                                                 log.error("Missing condition " + conditionId); 
    1082                                         } 
    1083                                          
    1084                                         // TODO: this code assumes the condition contains 
    1085                                         // a simple xpath 
    1086                                         log.debug("Using condition" + XmlUtils.marshaltoString(c, true, true)); 
    1087                                         xpathObj = getXPathFromCondition(c); 
    1088                                         thisXPath = xpathObj.getDataBinding().getXpath(); 
    1089                                          
    1090                                 } else if (repeatId!=null) {                     
    1091  
    1092                                         xpathObj = XPathsPart.getXPathById(xPaths, repeatId); 
    1093                                         thisXPath = xpathObj.getDataBinding().getXpath(); 
    1094                                          
    1095                                 } else { 
    1096                                          
    1097                                         log.warn("couldn't find binding or bindingrole!");  
    1098                                         // not all sdt's need have a binding;  
    1099                                         // they could be present in the docx for other purposes 
    1100                                         return; 
    1101                                 } 
    1102                                  
     1124 
     1125                                // TODO: this code assumes the condition contains 
     1126                                // a simple xpath 
     1127                                log.debug("Using condition" 
     1128                                                + XmlUtils.marshaltoString(c, true, true)); 
     1129                                xpathObj = getXPathFromCondition(c); 
     1130                                thisXPath = xpathObj.getDataBinding().getXpath(); 
     1131 
     1132                        } else if (repeatId != null) { 
     1133 
     1134                                xpathObj = XPathsPart.getXPathById(xPaths, repeatId); 
     1135                                thisXPath = xpathObj.getDataBinding().getXpath(); 
     1136 
    11031137                        } else { 
    1104                                 thisXPath = binding.getXpath();                  
    1105  
    1106                                 // Set this stuff up now 
    1107                                 bindingId = map.get(BINDING_ROLE_XPATH); 
    1108                                 xpathObj = XPathsPart.getXPathById(xPaths, bindingId); 
    1109                                  
    1110                                 // Sanity test 
    1111                                 if (!thisXPath.equals(xpathObj.getDataBinding().getXpath())) { 
    1112                                         log.error("XPaths didn't match for id " + bindingId + ": "  
    1113                                                         + xpathObj.getDataBinding().getXpath() ); 
    1114                                 } 
    1115                         } 
    1116                                          
    1117                         final String newPath = enhanceXPath(xpathBase, index + 1, thisXPath); 
    1118                          
    1119                         if (log.isDebugEnabled() && ! thisXPath.equals(newPath)) { 
    1120                           log.debug("xpath prefix enhanced " + thisXPath + " to " + newPath); 
    1121                         } 
    1122                          
    1123                         if (binding==null) { 
    1124                                                                  
    1125                                 if (conditionId!=null) { 
    1126                                          
    1127                                         // Create and add new condition 
    1128                                         Condition newCondition = XmlUtils.deepCopy(c); 
    1129                                         String newConditionId = conditionId + "_" + index; 
    1130                                         newCondition.setId(newConditionId);                                      
    1131                                         conditions.getCondition().add(newCondition); 
    1132                                          
    1133                                         // set sdt to use it 
    1134                                         map.put(BINDING_ROLE_CONDITIONAL, newConditionId); 
    1135                                         tag.setVal(QueryString.create(map)); 
    1136                                          
    1137                                         // TODO: this code assumes the condition contains 
    1138                                         // a simple xpath 
    1139                                          
    1140                                         // Clone the condition's xpath 
    1141                                         org.opendope.xpaths.Xpaths.Xpath newXPathObj = createNewXPathObject(newPath,  
    1142                                                         xpathObj, index); 
    1143                                          
    1144                                         // Use it 
    1145                                         newCondition.getXpathref().setId(newXPathObj.getId());                                   
    1146                                          
    1147                                 } else if (repeatId!=null) {                     
    1148                          
    1149                                         // Create the new xpath object 
    1150                                         org.opendope.xpaths.Xpaths.Xpath newXPathObj = createNewXPathObject(newPath,  
    1151                                                         xpathObj, index); 
    1152  
    1153                                         // set sdt to use it 
    1154                                         map.put(BINDING_ROLE_REPEAT, newXPathObj.getId());                                       
    1155                                         tag.setVal(QueryString.create(map)); 
    1156                                 } 
    1157                                  
    1158                         } else { 
    1159                                 binding.setXpath(newPath); 
    1160                                  
    1161                                 // Also need to create new xpath id, and add that 
    1162                                 org.opendope.xpaths.Xpaths.Xpath newXPathObj = createNewXPathObject(newPath,  
    1163                                                 xpathObj, index); 
     1138 
     1139                                log.warn("couldn't find binding or bindingrole!"); 
     1140                                // not all sdt's need have a binding; 
     1141                                // they could be present in the docx for other purposes 
     1142                                return; 
     1143                        } 
     1144 
     1145                } else { 
     1146                        thisXPath = binding.getXpath(); 
     1147 
     1148                        // Set this stuff up now 
     1149                        bindingId = map.get(BINDING_ROLE_XPATH); 
     1150                        xpathObj = XPathsPart.getXPathById(xPaths, bindingId); 
     1151 
     1152                        // Sanity test 
     1153                        if (!thisXPath.equals(xpathObj.getDataBinding().getXpath())) { 
     1154                                log.error("XPaths didn't match for id " + bindingId + ": " 
     1155                                                + xpathObj.getDataBinding().getXpath()); 
     1156                        } 
     1157                } 
     1158 
     1159                final String newPath = enhanceXPath(xpathBase, index + 1, thisXPath); 
     1160 
     1161                if (log.isDebugEnabled() && !thisXPath.equals(newPath)) { 
     1162                        log.debug("xpath prefix enhanced " + thisXPath + " to " + newPath); 
     1163                } 
     1164 
     1165                if (binding == null) { 
     1166 
     1167                        if (conditionId != null) { 
     1168 
     1169                                // Create and add new condition 
     1170                                Condition newCondition = XmlUtils.deepCopy(c); 
     1171                                String newConditionId = conditionId + "_" + index; 
     1172                                newCondition.setId(newConditionId); 
     1173                                conditions.getCondition().add(newCondition); 
    11641174 
    11651175                                // set sdt to use it 
    1166                                 map.put(BINDING_ROLE_XPATH, newXPathObj.getId());                                        
     1176                                map.put(BINDING_ROLE_CONDITIONAL, newConditionId); 
    11671177                                tag.setVal(QueryString.create(map)); 
    1168                         } 
    1169                                  
    1170                 } 
    1171                  
    1172     private org.opendope.xpaths.Xpaths.Xpath createNewXPathObject(String newPath,  
    1173                                 org.opendope.xpaths.Xpaths.Xpath xpathObj, int index) { 
    1174                          
    1175                         org.opendope.xpaths.Xpaths.Xpath newXPathObj = XmlUtils.deepCopy(xpathObj); 
    1176                         String newXPathId = newXPathObj.getId() + "_" + index; 
    1177                         newXPathObj.setId(newXPathId); 
    1178                         newXPathObj.getDataBinding().setXpath(newPath); 
    1179                         xPaths.getXpath().add(newXPathObj); 
    1180                         return newXPathObj; 
    1181                 } 
    1182                  
    1183 //              private static boolean removeSdt(Object sdtParent, Object sdt) { 
    1184 //                       
    1185 //                      if (sdtParent instanceof org.docx4j.wml.Body) {                  
    1186 //                              return ((org.docx4j.wml.Body)sdtParent).getEGBlockLevelElts().remove(sdt); 
    1187 //                      } else if (sdtParent instanceof org.docx4j.wml.P) { 
    1188 //                              return ((org.docx4j.wml.P)sdtParent).getParagraphContent().remove(sdt); 
    1189 //                      } else if (sdtParent instanceof org.docx4j.wml.Tbl) { 
    1190 //                              return ((org.docx4j.wml.Tbl)sdtParent).getEGContentRowContent().remove(sdt); 
    1191 //                      } else if (sdtParent instanceof org.docx4j.wml.Tr) { 
    1192 //                              return ((org.docx4j.wml.Tr)sdtParent).getEGContentCellContent().remove(sdt); 
    1193 //                      } else if (sdtParent instanceof org.docx4j.wml.Tc) { 
    1194 //                              return ((org.docx4j.wml.Tc)sdtParent).getEGBlockLevelElts().remove(sdt); 
    1195 //              } else { 
    1196 //                      log.error("TODO: handle removal from parent " + sdtParent.getClass().getName() ); 
    1197 //                      return false; 
    1198 //              } 
    1199 //              } 
    1200  
    1201                 public static SdtPr getSdtPr(Object o) { 
    1202                          
    1203                         if (o instanceof org.docx4j.wml.SdtBlock ) { 
    1204                                 return ((org.docx4j.wml.SdtBlock)o).getSdtPr(); 
    1205                         } else if ( o instanceof org.docx4j.wml.SdtRun ) { // sdt in paragraph 
    1206                                 return ((org.docx4j.wml.SdtRun)o).getSdtPr(); 
    1207                         } else if (  o instanceof org.docx4j.wml.CTSdtRow ) { // sdt wrapping row 
    1208                                 return ((org.docx4j.wml.CTSdtRow)o).getSdtPr(); 
    1209                         } else if (o instanceof org.docx4j.wml.CTSdtCell ) { // sdt wrapping cell 
    1210                                 return ((org.docx4j.wml.CTSdtCell)o).getSdtPr(); 
    1211                         } else { 
    1212                                 log.warn("TODO: Handle " + o.getClass().getName() );                                     
    1213                         }                
     1178 
     1179                                // TODO: this code assumes the condition contains 
     1180                                // a simple xpath 
     1181 
     1182                                // Clone the condition's xpath 
     1183                                org.opendope.xpaths.Xpaths.Xpath newXPathObj = createNewXPathObject( 
     1184                                                newPath, xpathObj, index); 
     1185 
     1186                                // Use it 
     1187                                newCondition.getXpathref().setId(newXPathObj.getId()); 
     1188 
     1189                        } else if (repeatId != null) { 
     1190 
     1191                                // Create the new xpath object 
     1192                                org.opendope.xpaths.Xpaths.Xpath newXPathObj = createNewXPathObject( 
     1193                                                newPath, xpathObj, index); 
     1194 
     1195                                // set sdt to use it 
     1196                                map.put(BINDING_ROLE_REPEAT, newXPathObj.getId()); 
     1197                                tag.setVal(QueryString.create(map)); 
     1198                        } 
     1199 
     1200                } else { 
     1201                        binding.setXpath(newPath); 
     1202 
     1203                        // Also need to create new xpath id, and add that 
     1204                        org.opendope.xpaths.Xpaths.Xpath newXPathObj = createNewXPathObject( 
     1205                                        newPath, xpathObj, index); 
     1206 
     1207                        // set sdt to use it 
     1208                        map.put(BINDING_ROLE_XPATH, newXPathObj.getId()); 
     1209                        tag.setVal(QueryString.create(map)); 
     1210                } 
     1211 
     1212        } 
     1213 
     1214        private org.opendope.xpaths.Xpaths.Xpath createNewXPathObject( 
     1215                        String newPath, org.opendope.xpaths.Xpaths.Xpath xpathObj, int index) { 
     1216 
     1217                org.opendope.xpaths.Xpaths.Xpath newXPathObj = XmlUtils 
     1218                                .deepCopy(xpathObj); 
     1219                String newXPathId = newXPathObj.getId() + "_" + index; 
     1220                newXPathObj.setId(newXPathId); 
     1221                newXPathObj.getDataBinding().setXpath(newPath); 
     1222                xPaths.getXpath().add(newXPathObj); 
     1223                return newXPathObj; 
     1224        } 
     1225 
     1226        // private static boolean removeSdt(Object sdtParent, Object sdt) { 
     1227        // 
     1228        // if (sdtParent instanceof org.docx4j.wml.Body) { 
     1229        // return 
     1230        // ((org.docx4j.wml.Body)sdtParent).getEGBlockLevelElts().remove(sdt); 
     1231        // } else if (sdtParent instanceof org.docx4j.wml.P) { 
     1232        // return ((org.docx4j.wml.P)sdtParent).getParagraphContent().remove(sdt); 
     1233        // } else if (sdtParent instanceof org.docx4j.wml.Tbl) { 
     1234        // return 
     1235        // ((org.docx4j.wml.Tbl)sdtParent).getEGContentRowContent().remove(sdt); 
     1236        // } else if (sdtParent instanceof org.docx4j.wml.Tr) { 
     1237        // return 
     1238        // ((org.docx4j.wml.Tr)sdtParent).getEGContentCellContent().remove(sdt); 
     1239        // } else if (sdtParent instanceof org.docx4j.wml.Tc) { 
     1240        // return ((org.docx4j.wml.Tc)sdtParent).getEGBlockLevelElts().remove(sdt); 
     1241        // } else { 
     1242        // log.error("TODO: handle removal from parent " + 
     1243        // sdtParent.getClass().getName() ); 
     1244        // return false; 
     1245        // } 
     1246        // } 
     1247 
     1248        public static SdtPr getSdtPr(Object o) { 
     1249 
     1250                if (o instanceof org.docx4j.wml.SdtBlock) { 
     1251                        return ((org.docx4j.wml.SdtBlock) o).getSdtPr(); 
     1252                } else if (o instanceof org.docx4j.wml.SdtRun) { // sdt in paragraph 
     1253                        return ((org.docx4j.wml.SdtRun) o).getSdtPr(); 
     1254                } else if (o instanceof org.docx4j.wml.CTSdtRow) { // sdt wrapping row 
     1255                        return ((org.docx4j.wml.CTSdtRow) o).getSdtPr(); 
     1256                } else if (o instanceof org.docx4j.wml.CTSdtCell) { // sdt wrapping cell 
     1257                        return ((org.docx4j.wml.CTSdtCell) o).getSdtPr(); 
     1258                } else { 
     1259                        log.warn("TODO: Handle " + o.getClass().getName()); 
     1260                } 
     1261                return null; 
     1262 
     1263                /* 
     1264                 * Or could have done: 
     1265                 *  
     1266                 * Object o = XmlUtils.unwrap(raw); Method m = 
     1267                 * o.getClass().getDeclaredMethod("getSdtPr"); SdtPr sdtPr = 
     1268                 * (SdtPr)m.invoke(o); 
     1269                 */ 
     1270        } 
     1271 
     1272        // public static List<Object> getSdtContent(Object o) { 
     1273        // 
     1274        // if (o instanceof org.docx4j.wml.SdtBlock) { 
     1275        // return ((org.docx4j.wml.SdtBlock) 
     1276        // o).getSdtContent().getEGContentBlockContent(); 
     1277        // } else if (o instanceof org.docx4j.wml.SdtRun) { // sdt in paragraph 
     1278        // return ((org.docx4j.wml.SdtRun) o).getSdtContent().getParagraphContent(); 
     1279        // } else if (o instanceof org.docx4j.wml.CTSdtRow) { // sdt wrapping row 
     1280        // return ((org.docx4j.wml.CTSdtRow) 
     1281        // o).getSdtContent().getEGContentRowContent(); 
     1282        // } else if (o instanceof org.docx4j.wml.CTSdtCell) { // sdt wrapping cell 
     1283        // return 
     1284        // ((org.docx4j.wml.CTSdtCell)o).getSdtContent().getEGContentCellContent(); 
     1285        // } else { 
     1286        // log.warn("TODO: Handle " + o.getClass().getName() + 
     1287        // " (if that's an sdt)"); 
     1288        // } 
     1289        // return null; 
     1290        // } 
     1291 
     1292        private List<Node> xpathGetNodes( 
     1293                        Map<String, CustomXmlDataStoragePart> customXmlDataStorageParts, 
     1294                        String storeItemId, String xpath, String prefixMappings) { 
     1295 
     1296                CustomXmlDataStoragePart part = customXmlDataStorageParts 
     1297                                .get(storeItemId.toLowerCase()); 
     1298                if (part == null) { 
     1299                        log.error("Couldn't locate part by storeItemId " + storeItemId); 
    12141300                        return null; 
    1215                          
    1216                         /* Or could have done: 
    1217                          *  
    1218                          *      Object o = XmlUtils.unwrap(raw); 
    1219                                 Method m = o.getClass().getDeclaredMethod("getSdtPr"); 
    1220                                 SdtPr sdtPr = (SdtPr)m.invoke(o); 
    1221                          *  
    1222                          */ 
    1223                 } 
    1224                  
    1225 //              public static List<Object> getSdtContent(Object o) { 
    1226 //                       
    1227 //                      if (o instanceof org.docx4j.wml.SdtBlock) { 
    1228 //                              return ((org.docx4j.wml.SdtBlock) o).getSdtContent().getEGContentBlockContent(); 
    1229 //                      } else if (o instanceof org.docx4j.wml.SdtRun) { // sdt in paragraph 
    1230 //                              return ((org.docx4j.wml.SdtRun) o).getSdtContent().getParagraphContent(); 
    1231 //                      } else if (o instanceof org.docx4j.wml.CTSdtRow) { // sdt wrapping row 
    1232 //                              return ((org.docx4j.wml.CTSdtRow) o).getSdtContent().getEGContentRowContent(); 
    1233 //                      } else if (o instanceof org.docx4j.wml.CTSdtCell) { // sdt wrapping cell 
    1234 //                              return ((org.docx4j.wml.CTSdtCell)o).getSdtContent().getEGContentCellContent(); 
    1235 //                      } else { 
    1236 //                              log.warn("TODO: Handle " + o.getClass().getName() + " (if that's an sdt)"); 
    1237 //                      } 
    1238 //                      return null; 
    1239 //              } 
    1240                  
    1241                 private List<Node> xpathGetNodes(Map<String, CustomXmlDataStoragePart> customXmlDataStorageParts, 
    1242                                 String storeItemId, String xpath, String prefixMappings) { 
    1243                          
    1244                         CustomXmlDataStoragePart part = customXmlDataStorageParts.get(storeItemId.toLowerCase()); 
    1245                         if (part==null) { 
    1246                                 log.error("Couldn't locate part by storeItemId " + storeItemId); 
    1247                                 return null; 
    1248                         } 
    1249                         return part.getData().xpathGetNodes(xpath, prefixMappings); 
    1250                 } 
    1251          
     1301                } 
     1302                return part.getData().xpathGetNodes(xpath, prefixMappings); 
     1303        } 
     1304 
    12521305} 
Note: See TracChangeset for help on using the changeset viewer.