Changeset 1641 for trunk/docx4j/src/main/java/org
- Timestamp:
- 08/12/11 03:42:53 (10 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/docx4j/src/main/java/org/docx4j/model/datastorage/OpenDoPEHandler.java
r1640 r1641 13 13 import java.util.Map; 14 14 import java.util.Set; 15 import java.util.UUID;16 15 import java.util.regex.Matcher; 17 16 import java.util.regex.Pattern; 18 17 19 import javax.xml.bind.JAXBException;20 18 import javax.xml.namespace.NamespaceContext; 21 19 … … 24 22 import org.docx4j.TraversalUtil; 25 23 import org.docx4j.XmlUtils; 26 import org.docx4j.customXmlProperties.SchemaRefs;27 import org.docx4j.customXmlProperties.SchemaRefs.SchemaRef;28 24 import org.docx4j.jaxb.Context; 29 25 import org.docx4j.jaxb.NamespacePrefixMappings; … … 32 28 import org.docx4j.openpackaging.exceptions.Docx4JException; 33 29 import org.docx4j.openpackaging.exceptions.InvalidFormatException; 34 import org.docx4j.openpackaging.io.SaveToZipFile;35 30 import org.docx4j.openpackaging.packages.WordprocessingMLPackage; 36 31 import org.docx4j.openpackaging.parts.CustomXmlDataStoragePart; 37 import org.docx4j.openpackaging.parts.CustomXmlDataStoragePropertiesPart;38 32 import org.docx4j.openpackaging.parts.PartName; 39 33 import org.docx4j.openpackaging.parts.WordprocessingML.AlternativeFormatInputPart; … … 66 60 67 61 public 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 { 72 67 73 68 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) { 77 72 throw new Docx4JException("OpenDoPE XPaths part missing"); 78 73 } 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 95 93 private WordprocessingMLPackage wordMLPackage; 96 94 private ShallowTraversor shallowTraversor; 97 95 98 96 public final static String BINDING_ROLE_REPEAT = "od:repeat"; 99 97 public final static String BINDING_ROLE_CONDITIONAL = "od:condition"; … … 102 100 public final static String BINDING_ROLE_COMPONENT_BEFORE = "od:continuousBefore"; 103 101 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" 109 107 */ 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; 190 269 } 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; 197 290 } 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); 540 316 } else { 541 // log.warn("TODO: Handle " + o.getClass().getName() + " (if that's an sdt)"); 317 continuousBefore.add(Boolean.FALSE); 318 continuousBeforeIndex.addFirst(index); 542 319 } 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); 577 325 } 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 588 400 } 589 401 } 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); 607 632 } 633 608 634 } 609 635 } 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 630 701 List<Object> newContent = new ArrayList<Object>(); 631 702 newContent.add(sdt); 632 703 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) 647 729 List<Object> newContent = new ArrayList<Object>(); 648 730 newContent.add(sdt); 649 731 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) { 767 747 List<Object> newContent = new ArrayList<Object>(); 768 748 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 927 964 private List<Object> cloneRepeatSdt(Object sdt, String xpathBase, 928 965 int numRepeats) { … … 965 1002 return newContent; 966 1003 } 967 1004 968 1005 private void emptyRepeatTagValue(final Tag tag) { 969 1006 … … 982 1019 tag.setVal(emptyRepeatValue); 983 1020 } 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 1008 1066 } 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); 1029 1123 } 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 1103 1137 } 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); 1164 1174 1165 1175 // set sdt to use it 1166 map.put(BINDING_ROLE_ XPATH, newXPathObj.getId());1176 map.put(BINDING_ROLE_CONDITIONAL, newConditionId); 1167 1177 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); 1214 1300 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 1252 1305 }
Note: See TracChangeset
for help on using the changeset viewer.
