Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
Copyright 2010-2013, Plutext Pty Ltd. This file is part of docx4j. docx4j is licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
  
  package org.docx4j.model.datastorage;
  
  import static org.docx4j.model.datastorage.XPathEnhancerParser.enhanceXPath;
  
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  
  import org.slf4j.Logger;
  import org.docx4j.wml.P;
  import org.docx4j.wml.Tc;
  import org.w3c.dom.Node;
  
  public class OpenDoPEHandler {
  
  	private static Logger log = LoggerFactory.getLogger(OpenDoPEHandler.class);
  
  	public OpenDoPEHandler(WordprocessingMLPackage wordMLPackage)
  			throws Docx4JException {
  
  		this. = wordMLPackage;
  
  		if (wordMLPackage.getMainDocumentPart().getXPathsPart() == null) {
  			throw new Docx4JException("OpenDoPE XPaths part missing");
  		} else {
  			 = wordMLPackage.getMainDocumentPart().getXPathsPart()
  			.debug(XmlUtils.marshaltoString(truetrue));
  		}
  		if (wordMLPackage.getMainDocumentPart().getConditionsPart() != null) {
  			 = wordMLPackage.getMainDocumentPart()
 			.debug(XmlUtils.marshaltoString(truetrue));
 		}
 		if (wordMLPackage.getMainDocumentPart().getComponentsPart() != null) {
 			 = wordMLPackage.getMainDocumentPart()
 			.debug(XmlUtils.marshaltoString(truetrue));
 		}
 
 		. = wordMLPackage;
 	}
 
 
 	public final static String BINDING_ROLE_XPATH = "od:xpath";
 
 	public final static String BINDING_ROLE_CONDITIONAL = "od:condition";
 	public final static String BINDING_RESULT_CONDITION_FALSE = "od:resultConditionFalse";
 
 	public final static String BINDING_ROLE_REPEAT = "od:repeat";
 	public final static String BINDING_RESULT_RPTD_ZERO = "od:resultRepeatZero";
 	public final static String BINDING_RESULT_RPTD = "od:rptd";
 	
 	// Repeat position condition (eg second last entry)
 	public final static String BINDING_ROLE_RPT_POS_CON = "od:RptPosCon";  // see bind.xslt
 
 	public final static String BINDING_ROLE_NARRATIVE = "od:narrative";
 
 	public final static String BINDING_ROLE_COMPONENT = "od:component";
 	public final static String BINDING_ROLE_COMPONENT_BEFORE = "od:continuousBefore";
 	public final static String BINDING_ROLE_COMPONENT_AFTER = "od:continuousAfter";
 
 	public final static String BINDING_CONTENTTYPE = "od:ContentType";
 	public final static String BINDING_HANDLER = "od:Handler";
 	public final static String BINDING_PROGID = "od:progid"// eg =Word.Document
 	/*
 	 * --------------------------------------------------------------------------
 	 * - Pre-processing of content controls which have a tag containing
 	 * "bindingrole"
 	 */
 
 
 // TODO consider whether to reinstate.  User would need to choose between 	
 //  conditional SDT removal, and reverting functionality.
 	
 //	private boolean removeSdtCellsOnFailedCondition;
 //
 //	/**
 //	 * Configure, how the preprocessor handles conditions on table cells.
 //	 *
 //	 * If set to <code>false</code>, conditional SDT cells are replaced by empty
 //	 * cells. This is the default behavior.
 //	 *
 //	 * If set to <code>true</code>, conditional SDT cells are removed entirely.
 //	 * Note that the table geometry is not changed; hence this works better
 //	 * without dynamic table widths / no global width settings.
 //	 *
 //	 * This affects all future calls on the {@link #preprocess} method for this
 //     * instance.
 //	 *
 //	 * @param removeSdtCellsOnFailedCondition
 //	 *            The new value for the cell removal flag.
 //	 */
 //	public void setRemoveSdtCellsOnFailedCondition(
 //			boolean removeSdtCellsOnFailedCondition) {
 //		this.removeSdtCellsOnFailedCondition = removeSdtCellsOnFailedCondition;
 //	}
 
Preprocess content controls which have tag "od:condition|od:repeat|od:component". It is "preprocess" in the sense that it is "pre" opening in Word The algorithm is as follows: Inject components first. Look at each top level SDT (ShallowTraversor). If it does not have a real data binding, it might have a bindingrole tag we need to process (processBindingRoleIfAny). Conditionals are easy. processRepeat method: - clones the sdt n times - invokes DeepTraversor which changes xpath binding on descendant sdts (both sdts with real bindings and sdts with bindingrole tags). It is not the job of DeepTraversor to expand out any other repeats it might encounter, or to resolve conditionals. Those things are done by ShallowTraversor, to which control returns, as it continues its traverse. The implementation of 13 Sept 2010 replaced the previous XPath based implementation, which did not support nested repeats. I've chosen to build this around TraversalUtil, instead of using XSLT, and this seems to have worked out nicely. The implementation of 10 October 2010 replaced the v1 conventions implementation with a v2 implementation. The main method in this class can convert v1 documents to v2. The v2 implementation is not yet complete. All v1 features are implemented, but not the new v2 stuff (eg complex conditions).

Parameters:
documentPart
Throws:
java.lang.Exception
 
 
 		do {
 			// A component can apply in both the main document part,
 			// and in headers/footers. See further
 			// http://forums.opendope.org/Support-components-in-headers-footers-tp2964174p2964174.html
 			// A component added to the
 			// main document part could add new headers/footers.
 			// So we need to work out what parts to preprocess
 			// here inside this do loop.
 
 			// Process repeats and conditionals.
 			try {
 				for (ContentAccessor part : partList) {
 				}
 			} catch (InputIntegrityException iie) { // RuntimeException
 				throw new Docx4JException(iie.getMessage(), iie);
 			}
 
 			// Convert any sdt with <w:tag w:val="od:component=comp1"/>
 			// to altChunk, and for MergeDocx users, to
 			// real WordML.
 			for (ContentAccessor part : partList) {
 			}
 
 		} while ();
 		// ie repeat the whole process if you got a component
 
 		return ;
 	}
 
 	private boolean justGotAComponent = false;
 
 	private static DocxFetcher docxFetcher;
 
 	public static DocxFetcher getDocxFetcher() {
 		return ;
 	}
 
 	public static void setDocxFetcher(DocxFetcher docxFetcher) {
 		. = docxFetcher;
 	}
 
 	protected static Set<ContentAccessorgetParts(WordprocessingMLPackage srcPackage) {
 
 		Set<ContentAccessorpartList = new HashSet<ContentAccessor>();
 
 		partList.add(srcPackage.getMainDocumentPart());
 
 		// Add headers/footers
 
 				partList.add((HeaderPartrp.getPart(r));
 			} else if (r.getType().equals(.)) {
 				partList.add((FooterPartrp.getPart(r));
 			}
 		}
 
 		return partList;
 	}
 
 			WordprocessingMLPackage srcPackageContentAccessor contentAccessor)
 			throws Docx4JException {
 
 		// convert components to altChunk
 		Map<IntegerCTAltChunkreplacements = new HashMap<IntegerCTAltChunk>();
 		Integer index = 0;
 		 = false;
 
 		LinkedList<IntegercontinuousBeforeIndex = new LinkedList<Integer>();
 		List<BooleancontinuousBefore = new ArrayList<Boolean>();
 
 		List<BooleancontinuousAfter = new ArrayList<Boolean>();
 
 		for (Object block : contentAccessor.getContent()) {
 
 			// Object ublock = XmlUtils.unwrap(block);
 			if (block instanceof org.docx4j.wml.SdtBlock) {
 
 				org.docx4j.wml.SdtBlock sdt = (org.docx4j.wml.SdtBlockblock;
 
 				Tag tag = getSdtPr(sdt).getTag();
 
 				if (tag == null) {
 					List<ObjectnewContent = new ArrayList<Object>();
 					newContent.add(sdt);
 					continue;
 				}
 
 				.info(tag.getVal());
 
 				HashMap<StringStringmap = QueryString.parseQueryString(tag.getVal(),
 						true);
 
 				String componentId = map.get();
 				if (componentId == null)
 					continue;
 
 				// Convert the sdt to a w:altChunk
 				// .. get the IRI
 				String iri = ComponentsPart.getComponentById(,
 						componentId).getIri();
 				.debug("Fetching " + iri);
 
 				if ( == null) {
 					.error("You need a docxFetcher (and the MergeDocx extension) to fetch components");
 					return srcPackage;
 				}
 
 				// .. create the part
 						getNewPartName("/chunk"".docx"srcPackage
 
 						"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")); // docx
 
 				Relationship altChunkRel = srcPackage.getMainDocumentPart()
 						.addTargetPart(afiPart);
 				CTAltChunk ac = Context.getWmlObjectFactory()
 				ac.setId(altChunkRel.getId());
 
 				replacements.put(indexac);
 
 				/*
 				 * 2011 12 11 TODO.  Rethink support for
 				 * od:continuousBefore and od:continuousAfter.
 				 */
 
 				// This is handled in this class
 				if (map.get() != null
 								.equals("true")) {
 					continuousBefore.add(.);
 					continuousBeforeIndex.addFirst(index);
 					.info("ctsBefore index: " + index);
 				} else {
 					continuousBefore.add(.);
 					continuousBeforeIndex.addFirst(index);
 				}
 
 				// The following is handled in ProcessAltChunk
 				if (map.get() != null
 					continuousAfter.add(.);
 				} else {
 					continuousAfter.add(.);
 				}
 
 			}
 			index++;
 		}
 
 			return srcPackage;
 		}
 
 		// Now replace in list
 		for (Integer key : replacements.keySet()) {
 			contentAccessor.getContent().set(keyreplacements.get(key));
 		}
 
 		// Go through docx in reverse order
 		List<ObjectbodyChildren = contentAccessor.getContent();
 		int i = 0;
 		for (Integer indexIntoBody : continuousBeforeIndex) {
 
 			if (continuousBefore.get(i)) {
 				// Element before the w:altChunk
 				if (indexIntoBody == 0) {
 					// // Insert a sectPr right at the beginning of the docx?
 					// // TODO check this isn't necessary
 					// SectPr newSectPr =
 					// Context.getWmlObjectFactory().createSectPr();
 					// SectPr.Type type =
 					// Context.getWmlObjectFactory().createSectPrType();
 					// type.setVal("continuous");
 					// newSectPr.setType( type );
 					//
 					// bodyChildren.add(0, newSectPr);
 
 				} else {
 					Object block = bodyChildren
 							.get(indexIntoBody.intValue() - 1);
 					if (block instanceof P && ((Pblock).getPPr() != null
 							&& ((Pblock).getPPr().getSectPr() != null) {
 						makeContinuous(((Pblock).getPPr().getSectPr());
 					} else if (block instanceof P) {
 						// More likely
 						PPr ppr = ((Pblock).getPPr();
 						if (ppr == null) {
 							ppr = Context.getWmlObjectFactory().createPPr();
 							((Pblock).setPPr(ppr);
 						}
 						SectPr newSectPr = Context.getWmlObjectFactory()
 						SectPr.Type type = Context.getWmlObjectFactory()
 						type.setVal("continuous");
 						newSectPr.setType(type);
 
 						ppr.setSectPr(newSectPr);
 					} else {
 						// Equally likely - its a table or something, so add a p
 						P newP = Context.getWmlObjectFactory().createP();
 						PPr ppr = Context.getWmlObjectFactory().createPPr();
 						newP.setPPr(ppr);
 
 						SectPr newSectPr = Context.getWmlObjectFactory()
 						SectPr.Type type = Context.getWmlObjectFactory()
 						type.setVal("continuous");
 						newSectPr.setType(type);
 						ppr.setSectPr(newSectPr);
 
 						bodyChildren.add(indexIntoBody.intValue(), newP); // add
 																			// before
 																			// altChunk
 					}
 				}
 			}
 			// else nothing specified, so go with normal MergeDocx behaviour
 
 			i++;
 		}
 
 		// process altChunk
 		try {
 			// Use reflection, so docx4j can be built
 			// by users who don't have the MergeDocx utility
 			Class<?> documentBuilder = Class
 					.forName("com.plutext.merge.ProcessAltChunk");
 			// Method method = documentBuilder.getMethod("merge",
 			// wmlPkgList.getClass());
 			Method[] methods = documentBuilder.getMethods();
 			Method processMethod = null;
 			for (int j = 0; j < methods.lengthj++) {
 				.debug(methods[j].getName());
 				if (methods[j].getName().equals("process")) {
 					processMethod = methods[j];
 				}
 			}
 			if (processMethod == null )
 				throw new NoSuchMethodException();
 			return (WordprocessingMLPackageprocessMethod.invoke(null,
 					srcPackage);
 
 		} catch (ClassNotFoundException e) {
 			 = false;
 			return srcPackage;
 			// throw new Docx4JException("Problem processing w:altChunk", e);
 		} catch (NoSuchMethodException e) {
 			// Degrade gracefully
 			 = false;
 			return srcPackage;
 			// throw new Docx4JException("Problem processing w:altChunk", e);
 		} catch (Exception e) {
 			throw new Docx4JException("Problem processing w:altChunk"e);
 		}
 	}
 
 	public void makeContinuous(SectPr sectPr) {
 
 		if (sectPr == null) {
 			.warn("sectPr was null");
 			return;
 		}
 
 		type.setVal("continuous");
 		sectPr.setType(type);
 
 		// columns, endnotes, footnotes, formprot, line numbers are OK
 
 		// null out certain page level section properties
 		sectPr.setBidi(null);
 		sectPr.setDocGrid(null);
 		sectPr.setPaperSrc(null);
 		sectPr.setPgBorders(null);
 		sectPr.setPgMar(null);
 		sectPr.setPgNumType(null);
 		sectPr.setPgSz(null);
 		sectPr.setPrinterSettings(null);
 		sectPr.setSectPrChange(null);
 		sectPr.setTitlePg(null);
 		sectPr.setVAlign(null);
 	}
 
 	private PartName getNewPartName(String prefixString suffix,
 
 		PartName proposed = null;
 		int i = 1;
 		do {
 
 			if (i > 1) {
 				proposed = new PartName(prefix + i + suffix);
 			} else {
 				proposed = new PartName(prefix + suffix);
 			}
 			i++;
 
 		} while (rp.getRel(proposed) != null);
 
 		return proposed;
 
 	}
 
 	public void extensionMissing(Exception e) {
 		.error("\n" + e.getClass().getName() + ": " + e.getMessage() + "\n");
 		.error("* You don't appear to have the MergeDocx paid extension,");
 		.error("* which is necessary to merge docx, or process altChunk.");
 		.error("* Purchases of this extension support the docx4j project.");
 		.error("* Please visit www.plutext.com if you want to buy it.");
 	}
 
 	// private static void preprocessRun(WordprocessingMLPackage wordMLPackage,
 	// ContentAccessor content) throws Docx4JException {
 	//
 	// log.info("\n\n Preprocess run.. \n\n");
 	//
 	// MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
 	//
 	// if (wordMLPackage.getMainDocumentPart().getXPathsPart()==null) {
 	// throw new Docx4JException("OpenDoPE XPaths part missing");
 	// } else {
 	// xPaths =
 	// wordMLPackage.getMainDocumentPart().getXPathsPart().getJaxbElement();
 	// log.debug( XmlUtils.marshaltoString(xPaths, true, true));
 	// }
 	// if (wordMLPackage.getMainDocumentPart().getConditionsPart()!=null) {
 	// conditions =
 	// wordMLPackage.getMainDocumentPart().getConditionsPart().getJaxbElement();
 	// log.debug( XmlUtils.marshaltoString(conditions, true, true));
 	// }
 	// if (wordMLPackage.getMainDocumentPart().getComponentsPart()!=null) {
 	// components =
 	// wordMLPackage.getMainDocumentPart().getComponentsPart().getJaxbElement();
 	// log.debug( XmlUtils.marshaltoString(components, true, true));
 	// }
 	//
 	// org.docx4j.wml.Document wmlDocumentEl =
 	// (org.docx4j.wml.Document)documentPart.getJaxbElement();
 	// Body body = wmlDocumentEl.getBody();
 	//
 	// shallowTraversor.wordMLPackage = wordMLPackage;
 	//
 	// new TraversalUtil(body, shallowTraversor);
 	//
 	// }
 
 	// private static XPathsPart getXPathsPart(WordprocessingMLPackage
 	// wordMLPackage) {
 	// wordMLPackage.getParts().
 	// }
 
This traversor duplicates the repeats, and removes false conditonals
 
 	private class ShallowTraversor implements TraversalUtil.Callback {
 
 		// private static Logger log = LoggerFactory.getLogger(ShallowTraversor.class);
 
 
 		public List<Objectapply(Object wrappedthrows RuntimeException {
 
 			// apply processSdt to any sdt
 			// which might be a conditional|repeat
 			Object o = XmlUtils.unwrap(wrapped);
 			if (o instanceof org.docx4j.wml.SdtBlock
 					|| o instanceof org.docx4j.wml.SdtRun
 					|| o instanceof org.docx4j.wml.CTSdtRow
 					|| o instanceof org.docx4j.wml.CTSdtCell) {
 
 				if (getSdtPr(o).getDataBinding() == null) {
 					// a real binding attribute trumps any tag
 				}
 
 			} else {
 				// log.warn("TODO: Handle " + o.getClass().getName() +
 				// " (if that's an sdt)");
 			}
 
 			// Otherwise just preserve the content
 			List<ObjectnewContent = new ArrayList<Object>();
 
 			newContent.add(wrapped); // we want the JAXBElement (if any)
 			return newContent;
 
 		}
 
 		public boolean shouldTraverse(Object o) {
 
 			// we want to traverse all sdts, since an sdt which
 			// doesn't have a binding role might contain one
 			// which does
 
 			return true;
 		}
 
 		public List<ObjectgetChildren(Object o) {
 			return TraversalUtil.getChildrenImpl(o);
 		}
 
 		public void walkJAXBElements(Object parent) {
 			// Breadth first
 
 			List<ObjectnewChildren = new ArrayList<Object>();
 
 			Object parentUnwrapped = XmlUtils.unwrap(parent);
 			List<Objectchildren = getChildren(parentUnwrapped);
 			if (children == null) {
 				.debug("no children: " + parentUnwrapped.getClass().getName());
 				return;
 			} else {
 				for (Object o : children) {
 					newChildren.addAll(this.apply(o));
 				}
 			}
 			// Replace list, so we'll traverse all the new sdts we've just
 			// created
 			TraversalUtil.replaceChildren(parentUnwrappednewChildren);
 
 			children = getChildren(parentUnwrapped);
 			if (children == null) {
 				.debug("no children: " + parentUnwrapped.getClass().getName());
 			} else {
 				for (Object o : children) {
 
 					// *** this.apply(o);
 
 					if (this.shouldTraverse(o)) {
 					}
 
 				}
 			}
 		}
 
 	}

This applies to any sdt which might be a conditional|repeat

Parameters:
wordMLPackage
sdtParent
sdt
tag
sdtContent
Returns:
Throws:
java.lang.Exception
 
 			WordprocessingMLPackage wordMLPackageObject sdt) {
 		.debug("Processing " + getSdtPr(sdt).getId().getVal());
 		Tag tag = getSdtPr(sdt).getTag();
 
 		if (tag == null) {
 			List<ObjectnewContent = new ArrayList<Object>();
 			newContent.add(sdt);
 			return newContent;
 		}
 
 		.info(tag.getVal());
 
 		HashMap<StringStringmap = QueryString.parseQueryString(tag.getVal(), true);
 
 		String conditionId = map.get();
 		String repeatId = map.get();
 		if (conditionId == null && repeatId == null && xp == null) {
 			List<ObjectnewContent = new ArrayList<Object>();
 			newContent.add(sdt);
 			return newContent;
 		}
 
 		Map<StringCustomXmlPartcustomXmlDataStorageParts = wordMLPackage
 
 		if (conditionId != null) {
 
 			.info("Processing Conditional: " + tag.getVal());
 
 			// At present, this only handles simple conditions
 			Condition c = ConditionsPart.getConditionById(,
 					conditionId);
 			if (c == null) {
 				.error("Missing condition " + conditionId);
 			}
 
 			if ( c.evaluate(wordMLPackagecustomXmlDataStorageParts) ) {
 				.debug("so keeping");
 
 				List<ObjectnewContent = new ArrayList<Object>();
 				newContent.add(sdt);
 				return newContent;
 
 			} else {
 				return conditionFalse(sdt);
 			}
 
 		} else if (repeatId != null) {
 
 			.info("Processing Repeat: " + tag.getVal());
 
 			return processRepeat(sdt,
 					customXmlDataStoragePartswordMLPackage
 
 		} else if (xp != null) {
 
 			// Word can't handle an XPath that returns something
 			// other than an element
 			// eg string or boolean or number, so we'll need to work this out.
 			// In principal, we could do this in this pre-processing step,
 			// or via bind.xslt.
 
 			// Doing it here means the bind.xslt step can be restricted to pure
 			// Word-like processing.
 
 			// Doing it there means we can take advantage of the multiline
 			// processing we have there, and less code.
 			// So as from 13 Sept 2011 (what will be 2.7.1), do it there.
 
 			List<ObjectnewContent = new ArrayList<Object>();
 			newContent.add(sdt);
 			return newContent;
 		}
 		// shouldn't happen
 		return null;
 	}

Insert an empty placeholder SDT, to facilitate round-tripping (ie ability to convert instance docx back to original template), which you may wish to do if you want to insert updated data, but preserve certain manual edits.

Parameters:
sdt
Returns:
 	
 	private List<ObjectconditionFalse(Object sdt) {
 
 		List<ObjectnewContent = new ArrayList<Object>();
 		
 //		if (removeSdtCellsOnFailedCondition) {
 //		// backward compatibility
 //		// NB this is not compatible with reverting functionality, and
 //		// will break if the result is an empty tc
 //		return newContent;
 //	}
 		
 		newContent.add(sdt);
 
 		// Change the tag to od:resultConditionFalse
 		SdtPr sdtPr = getSdtPr(sdt);
 
 		CTDataBinding binding = sdtPr.getDataBinding();
 		if (binding != null) {  // Shouldn't be a binding anyway
 			sdtPr.getRPrOrAliasOrLock().remove(binding);
 		}
 
 		Tag tag = sdtPr.getTag(); 
 
 		final String tagVal = tag.getVal();
 		final Pattern stripConditionArgPattern = Pattern
 				.compile("(.*od:condition=)([^&]*)(.*)");
 		final Matcher stripPatternMatcher = stripConditionArgPattern
 				.matcher(tagVal);
 		if (!stripPatternMatcher.matches()) {
 			.error("Cannot find condition tag in sdtPr/tag while setting conditionFalse; something is wrong with " + tagVal);
 			return newContent;
 		}
 		final String emptyConditionValue =  + "=" + stripPatternMatcher.group(2) + stripPatternMatcher.group(3);
 		tag.setVal(emptyConditionValue);	
 		
 		// Lock it
         CTLock lock = Context.getWmlObjectFactory().createCTLock(); 
         lock.setVal(....);		
         JAXBElement<org.docx4j.wml.CTLocklockWrapped = Context.getWmlObjectFactory().createSdtPrLock(lock); 
         sdtPr.getRPrOrAliasOrLock().addlockWrapped); // assumes no lock is there already
 
 		// Empty the content
         // .. OpenDoPEIntegrity fixes this where it is not OK, but
         // where it needs to insert a tc, it has no way of adding original tcPr, so
         // we handle this here
 		TcFinder tcFinder = new TcFinder();
 		new TraversalUtil(((SdtElement)sdt).getSdtContent().getContent(), tcFinder);
 		if (tcFinder.tcList.size()>0) {
 			Tc tc = tcFinder.tcList.get(0);
 			tc.getContent().clear();
 			P p = Context.getWmlObjectFactory().createP();
 			tc.getContent().add(p);			
 	        ((SdtElement)sdt).getSdtContent().getContent().clear();
 	        ((SdtElement)sdt).getSdtContent().getContent().add(tc);
 		} else {
 	        ((SdtElement)sdt).getSdtContent().getContent().clear();
 		}
 		
 		return newContent;		
 	}
 
 
 //	private Object obtainParent(Object sdt) {
 //		if (!(sdt instanceof Child))
 //			throw new IllegalArgumentException("Object of class "
 //					+ sdt.getClass().getName() + " is not a Child");
 //
 //		return ((Child) sdt).getParent();
 //	}
 //
 //	private int countContentChildren(final Object tc) {
 //		final List<Object> selfAndSiblings = obtainChildren(tc);
 //		int contentChildCount = 0;
 //		for (final Object child : selfAndSiblings)
 //			if (!(child instanceof TcPr))
 //				contentChildCount++;
 //		return contentChildCount;
 //	}
 
 //	private List<Object> obtainChildren(Object element) {
 //		Object unwrapped = XmlUtils.unwrap(element);
 //		if (unwrapped instanceof ContentAccessor) {
 //			return ((ContentAccessor) unwrapped).getContent();
 //		} else if (unwrapped instanceof SdtElement) {
 //			return ((SdtElement) unwrapped).getSdtContent().getContent();
 //		}
 //		log.warn("Don't know how to access the children of "
 //				+ (unwrapped == null ? "null" : unwrapped.getClass().getName()));
 //		return Collections.emptyList();
 //	}
 	 
 
 
 	private List<ObjectprocessRepeat(Object sdt,
 			Map<StringCustomXmlPartcustomXmlDataStorageParts,
 			XPathsPart xPathsPart) {
 
 		Tag tag = getSdtPr(sdt).getTag();
 
 		HashMap<StringStringmap = QueryString.parseQueryString(
 				tag.getVal(), true);
 
 		String repeatId = map.get();
 
 		// Check, whether we are in an old repeat case. These can be removed.
 		if (StringUtils.isEmpty(repeatId))
 			return new ArrayList<Object>();
 
 		org.opendope.xpaths.Xpaths.Xpath xpathObj = XPathsPart.getXPathById(
 				repeatId);
 
 		String storeItemId = xpathObj.getDataBinding().getStoreItemID();
 		String xpath = xpathObj.getDataBinding().getXpath();
 		String prefixMappings = xpathObj.getDataBinding().getPrefixMappings();
 
 		// Get the bound XML
 		String xpathBase;
 		// if (xpath.endsWith("/*")) {
 		// xpathBase = xpath.substring(0, xpath.length()-2);
 		// } else
 		if (xpath.endsWith("/")) {
 			xpathBase = xpath.substring(0, xpath.length() - 1);
 
 			// Check, whether the xpath ends with a [1]. If so, guess it comes
 			// from a round-tripped path and strip it
 		} else if (xpath.endsWith("[1]")) {
 			xpathBase = xpath.substring(0, xpath.length() - 3);
 
 		} else {
 			xpathBase = xpath;
 		}
 
 		// DON'T Drop any trailing position! That breaks nested repeats
 		// if (xpathBase.endsWith("]"))
 		// xpathBase = xpathBase.substring(0, xpathBase.lastIndexOf("["));
 
 		.info("/n/n Repeat: using xpath: " + xpathBase);
 		List<NoderepeatedSiblings = xpathGetNodes(customXmlDataStorageParts,
 				storeItemIdxpathBaseprefixMappings);
 		// storeItemId, xpathBase+"/*", prefixMappings);
 
 		// Count siblings
 		int numRepeats = repeatedSiblings.size();
 		.debug("yields REPEATS: " + numRepeats);
 
 		if (numRepeats == 0) {
 			//return new ArrayList<Object>(); // effectively, delete
 			
 			// Change tag to od:resultRepeatZero=id
 			return repeatZero(sdt);
 		}
 
 		// duplicate content here ...
 		List<Objectrepeated = cloneRepeatSdt(sdtxpathBasenumRepeats);
 
 		// deep traverse to fix binding
 		DeepTraversor dt = new DeepTraversor();
 		dt.xpathBase = xpathBase;
 		for (int i = 0; i < repeated.size(); i++) {
 
 			.info("\n Traversing clone " + i);
 
 			dt.index = i;
 			new TraversalUtil(repeated.get(i), dt);
 		}
 		.info(".. deep traversals done ");
 		
 		// make bookmarks (if any) unique
 		for (int i = 0; i < repeated.size(); i++) {
 			try {
 				
 				// Use the sdt id for uniqueness
 				long global= ((SdtElement)repeated.get(i)).getSdtPr().getId().getVal().longValue();
 				
 				BookmarkRenumber.fixRange(
 						((SdtElement)repeated.get(i)).getSdtContent().getContent(), 
 						"CTBookmark""CTMarkupRange"nullglobali);
 			} catch (Exception e) {
 				// Shouldn't happen .. TODO remove reflection?
 			}
 		}
 
 		return repeated;
 	}

Insert an empty placeholder SDT, to facilitate round-tripping (ie ability to convert instance docx back to original template), which you may wish to do if you want to insert updated data, but preserve certain manual edits.

Parameters:
sdt
Returns:
 
 	private List<ObjectrepeatZero(Object sdt) {
 
 		List<ObjectnewContent = new ArrayList<Object>();
 		
 //		if (removeSdtCellsOnFailedCondition) {
 //			// backward compatibility
 //			// NB this is not compatible with reverting functionality, and
 //			// will break if the result is an empty tc
 //			return newContent;
 //		}
 		
 		newContent.add(sdt);
 
 		// Change the tag to od:resultRepeatZero
 		SdtPr sdtPr = getSdtPr(sdt);
 
 		CTDataBinding binding = sdtPr.getDataBinding();
 		if (binding != null) {  // Shouldn't be a binding anyway
 			sdtPr.getRPrOrAliasOrLock().remove(binding);
 		}
 
 		Tag tag = sdtPr.getTag(); 
 
 		final String tagVal = tag.getVal();
 		final Pattern stripRepeatArgPattern = Pattern
 				.compile("(.*od:repeat=)([^&]*)(.*)");
 		final Matcher stripPatternMatcher = stripRepeatArgPattern
 				.matcher(tagVal);
 		if (!stripPatternMatcher.matches()) {
			.error("Cannot find repeat tag in sdtPr/tag while processing repeat; something is wrong with " + tagVal);
			return newContent;
		final String emptyRepeatValue =  + "=" + stripPatternMatcher.group(2) + stripPatternMatcher.group(3);
		tag.setVal(emptyRepeatValue);	
		// Lock it
        CTLock lock = Context.getWmlObjectFactory().createCTLock(); 
        JAXBElement<org.docx4j.wml.CTLocklockWrapped = Context.getWmlObjectFactory().createSdtPrLock(lock); 
        sdtPr.getRPrOrAliasOrLock().addlockWrapped); // assumes no lock is there already
		// Empty the content
        // .. OpenDoPEIntegrity fixes this where it is not OK, but
        // where it needs to insert a tc, it has no way of adding original tcPr, so
        // we handle this here
		TcFinder tcFinder = new TcFinder();
		if (tcFinder.tcList.size()>0) {
			Tc tc = tcFinder.tcList.get(0);
			P p = Context.getWmlObjectFactory().createP();
	        ((SdtElement)sdt).getSdtContent().getContent().clear();
	        ((SdtElement)sdt).getSdtContent().getContent().add(tc);
else {
	        ((SdtElement)sdt).getSdtContent().getContent().clear();
        
		return newContent;
	private List<ObjectcloneRepeatSdt(Object sdtString xpathBase,
			int numRepeats) {
		List<ObjectnewContent = new ArrayList<Object>();
		SdtPr sdtPr = getSdtPr(sdt);
		.debug(XmlUtils.marshaltoString(sdtPrtruetrue));
		// CTDataBinding binding =
		// (CTDataBinding)XmlUtils.unwrap(sdtPr.getDataBinding());
		CTDataBinding binding = sdtPr.getDataBinding();
		if (binding != null) {  // Shouldn't be a binding anyway
			sdtPr.getRPrOrAliasOrLock().remove(binding);
		emptyRepeatTagValue(sdtPr.getTag()); // 2012 07 15: do it to the first one
		for (int i = 0; i < numRepeatsi++) {
			// 2012 07 13: for "od:RptPosCon" processing to
			// work (conditional inclusion dependant on position
			// in repeat), we need each entry (ie including the
			// original) to have the same tag (which I've changed
			// to od:rptd).
			if (i > 0) {
				// Change ID
				sdtPr.setId();
// preserve ID on index 0, important for OpenDoPEReverter!
			// Clone
			newContent.add(XmlUtils.deepCopy(sdt));
		return newContent;
	private void emptyRepeatTagValue(final Tag tag) {
		final String tagVal = tag.getVal();
		final Pattern stripRepeatArgPattern = Pattern
				.compile("(.*od:repeat=)([^&]*)(.*)");
		final Matcher stripPatternMatcher = stripRepeatArgPattern
				.matcher(tagVal);
		if (!stripPatternMatcher.matches()) {
			.error("Cannot find repeat tag in sdtPr/tag while processing repeat; something is wrong with " + tagVal);
			return;
//		final String emptyRepeatValue = stripPatternMatcher.group(1)
//				+ stripPatternMatcher.group(3);
		final String emptyRepeatValue =  + "=" + stripPatternMatcher.group(2) + stripPatternMatcher.group(3);
		tag.setVal(emptyRepeatValue);
	class DeepTraversor implements TraversalUtil.Callback {
		// private static Logger log = LoggerFactory.getLogger(DeepTraversor.class);
		int index = 0;
		String xpathBase = null;
		public List<Objectapply(Object o) {
			// log.debug("apply for " + o.getClass().getName());
			if (o instanceof org.docx4j.wml.SdtBlock
					|| o instanceof org.docx4j.wml.SdtRun
					|| o instanceof org.docx4j.wml.CTSdtRow
					|| o instanceof org.docx4j.wml.CTSdtCell) { // SdtCell as
																// well, here
				// whether it has a databinding or not
else {
				// log.warn("TODO: Handle " + o.getClass().getName()
				// + " (if that's an sdt)");
			return null// doesn't matter in this implementation
		public void walkJAXBElements(Object parent) {
			List<Objectchildren = getChildren(parent);
			if (children != null) {
				for (Object o : children) {
					o = XmlUtils.unwrap(o);
					this.apply(o);
					if (this.shouldTraverse(o)) {
		public List<ObjectgetChildren(Object o) {
			// log.debug("getChildren for " + o.getClass().getName());
			return TraversalUtil.getChildrenImpl(o);
		public boolean shouldTraverse(Object o) {
			return true;
	private void processDescendantBindings(Object sdtString xpathBase,
			int index) {
		SdtPr sdtPr = getSdtPr(sdt);
		// log.debug(XmlUtils.marshaltoString(sdtPr, true, true));
		// Give it a unique ID (supersedes above?)
		sdtPr.setId();
		// log.debug(XmlUtils.marshaltoString(sdtPr, true, true));
		CTDataBinding binding = (CTDataBinding) XmlUtils.unwrap(sdtPr
		String thisXPath = null;
		// It'll have one of these three...
		String conditionId = null;
		String repeatId = null;
		String bindingId = null;
		org.opendope.xpaths.Xpaths.Xpath xpathObj = null;
		Tag tag = sdtPr.getTag();
		if (tag == null)
			return;
		HashMap<StringStringmap = QueryString.parseQueryString(
				tag.getVal(), true);
		if (binding == null) {
			conditionId = map.get();
			repeatId = map.get();
			if (conditionId != null) {
//				c = ConditionsPart.getConditionById(conditions, conditionId);
//				if (c == null) {
//					log.error("Missing condition " + conditionId);
//					throw new InputIntegrityException("Required condition '" + conditionId + "' is missing");
//				}
//				// TODO: this code assumes the condition contains
//				// a simple xpath
//				log.debug("Using condition"
//						+ XmlUtils.marshaltoString(c, true, true));
//				xpathObj = getXPathFromCondition(c);
//				thisXPath = xpathObj.getDataBinding().getXpath();
				processDescendantConditionsdt,  xpathBase,
						 index,  tag );
				return;
else if (repeatId != null) {
				xpathObj = XPathsPart.getXPathById(repeatId);
				thisXPath = xpathObj.getDataBinding().getXpath();
				xpathObj = XPathsPart.getXPathById(,
				thisXPath = xpathObj.getDataBinding().getXpath();
else {
				.warn("couldn't find binding or bindingrole!");
				// not all sdt's need have a binding;
				// they could be present in the docx for other purposes
				return;
				// NB an OpenDoPE xpath tag (with no w:binding element)
				// eg as created by authoring tool for a "count(" XPath
				// ends up here.
else {
			thisXPath = binding.getXpath();
			// Set this stuff up now
			bindingId = map.get();
			xpathObj = XPathsPart.getXPathById(bindingId);
			// Sanity test
			if (!thisXPath.equals(xpathObj.getDataBinding().getXpath())) {