DocumentUtil.java
package pro.verron.officestamper.core;
import org.docx4j.XmlUtils;
import org.docx4j.wml.*;
import org.jvnet.jaxb2_commons.ppp.Child;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.verron.officestamper.api.OfficeStamperException;
import static java.util.Collections.emptyList;
/// Utility class to retrieve elements from a document.
///
/// @author Joseph Verron
/// @author DallanMC
/// @version ${version}
/// @since 1.4.7
public class DocumentUtil {
private static final Logger log = LoggerFactory.getLogger(DocumentUtil.class);
private DocumentUtil() {
throw new OfficeStamperException("Utility classes shouldn't be instantiated");
}
/// Finds the smallest common parent between two objects.
///
/// @param o1 the first object
/// @param o2 the second object
///
/// @return the smallest common parent of the two objects
///
/// @throws OfficeStamperException if there is an error finding the common parent
public static ContentAccessor findSmallestCommonParent(Object o1, Object o2) {
if (depthElementSearch(o1, o2) && o2 instanceof ContentAccessor contentAccessor)
return findInsertableParent(contentAccessor);
else if (o2 instanceof Child child) return findSmallestCommonParent(o1, child.getParent());
else throw new OfficeStamperException();
}
/// Recursively searches for an element in a content tree.
///
/// @param searchTarget the element to search for
/// @param searchTree the content tree to search in
///
/// @return true if the element is found, false otherwise
public static boolean depthElementSearch(Object searchTarget, Object searchTree) {
var element = XmlUtils.unwrap(searchTree);
if (searchTarget.equals(element)) return true;
var contentContent = switch (element) {
case ContentAccessor accessor -> accessor.getContent();
case SdtRun sdtRun -> sdtRun.getSdtContent()
.getContent();
case ProofErr _, Text _, R.CommentReference _, CommentRangeEnd _, CommentRangeStart _, Br _,
R.LastRenderedPageBreak _, CTBookmark _ -> emptyList();
default -> {
log.warn("Element {} not recognized", element);
yield emptyList();
}
};
return contentContent.stream()
.anyMatch(obj -> depthElementSearch(searchTarget, obj));
}
private static ContentAccessor findInsertableParent(Object searchFrom) {
return switch (searchFrom) {
case Tc tc -> tc;
case Body body -> body;
case Child child -> findInsertableParent(child.getParent());
default -> throw new OfficeStamperException("Unexpected parent " + searchFrom.getClass());
};
}
}