DocumentWalker.java

package pro.verron.officestamper.core;

import org.docx4j.XmlUtils;
import org.docx4j.wml.*;
import org.docx4j.wml.R.CommentReference;
import pro.verron.officestamper.api.DocxPart;

/**
 * This class walks the document and calls abstract methods for each element it encounters.
 * The following elements are supported:
 * <ul>
 * <li>{@link P}</li>
 * <li>{@link R}</li>
 * <li>{@link Tbl}</li>
 * <li>{@link Tr}</li>
 * <li>{@link Tc}</li>
 * <li>{@link CommentRangeStart}</li>
 * <li>{@link CommentRangeEnd}</li>
 * <li>{@link CommentReference}</li>
 * </ul>
 * The following elements are not supported:
 * <ul>
 * <li>{@link SdtBlock}</li>
 * <li>{@link SdtRun}</li>
 * <li>{@link SdtElement}</li>
 * <li>{@link CTSimpleField}</li>
 * <li>{@link CTSdtCell}</li>
 * <li>{@link CTSdtContentCell}</li>
 * </ul>
 *
 * @author Joseph Verron
 * @author Tom Hombergs
 * @version ${version}
 * @since 1.0.0
 */
public abstract class DocumentWalker {

    private final DocxPart source;

    /**
     * Creates a new DocumentWalker that will traverse the given document.
     *
     * @param source the document to traverse.
     */
    protected DocumentWalker(DocxPart source) {
        this.source = source;
    }

    /**
     * Starts the traversal of the document.
     */
    public void walk() {
        for (Object content : source.content()) {
            Object ue = XmlUtils.unwrap(content);
            if (ue instanceof P o) walkParagraph(o);
            else if (ue instanceof R o) walkRun(o);
            else if (ue instanceof Tbl o) walkTable(o);
            else if (ue instanceof Tr o) walkTableRow(o);
            else if (ue instanceof Tc o) walkTableCell(o);
            else if (ue instanceof CommentRangeStart o) onCommentRangeStart(o);
            else if (ue instanceof CommentRangeEnd o) onCommentRangeEnd(o);
            else if (ue instanceof CommentReference o) onCommentReference(o);
        }
    }

    private void walkTable(Tbl table) {
        onTable(table);
        for (Object contentElement : table.getContent()) {
            Object unwrappedObject = XmlUtils.unwrap(contentElement);
            if (unwrappedObject instanceof Tr row) {
                walkTableRow(row);
            }
        }
    }

    private void walkTableRow(Tr row) {
        onTableRow(row);
        for (Object rowContentElement : row.getContent()) {
            Object unwrappedObject = XmlUtils.unwrap(rowContentElement);
            if (unwrappedObject instanceof Tc cell) {
                walkTableCell(cell);
            }
        }
    }

    private void walkTableCell(Tc cell) {
        onTableCell(cell);
        for (Object cellContentElement : cell.getContent()) {
            Object unwrappedObject = XmlUtils.unwrap(cellContentElement);
            if (unwrappedObject instanceof P) {
                P p = (P) cellContentElement;
                walkParagraph(p);
            }
            else if (unwrappedObject instanceof R) {
                R r = (R) cellContentElement;
                walkRun(r);
            }
            else if (unwrappedObject instanceof Tbl nestedTable) {
                walkTable(nestedTable);
            }
            else if (unwrappedObject instanceof CommentRangeStart commentRangeStart) {
                onCommentRangeStart(commentRangeStart);
            }
            else if (unwrappedObject instanceof CommentRangeEnd commentRangeEnd) {
                onCommentRangeEnd(commentRangeEnd);
            }
        }
    }

    private void walkParagraph(P p) {
        onParagraph(p);
        for (Object element : p.getContent()) {
            Object unwrappedObject = XmlUtils.unwrap(element);
            if (unwrappedObject instanceof R r) {
                walkRun(r);
            }
            else if (unwrappedObject instanceof CommentRangeStart commentRangeStart) {
                onCommentRangeStart(commentRangeStart);
            }
            else if (unwrappedObject instanceof CommentRangeEnd commentRangeEnd) {
                onCommentRangeEnd(commentRangeEnd);
            }
        }
    }

    private void walkRun(R r) {
        onRun(r);
        for (Object element : r.getContent()) {
            Object unwrappedObject = XmlUtils.unwrap(element);
            if (unwrappedObject instanceof CommentReference commentReference) {
                onCommentReference(commentReference);
            }
        }
    }

    /**
     * This method is called for every {@link R} element in the document.
     *
     * @param run the {@link R} element to process.
     */
    protected abstract void onRun(R run);

    /**
     * This method is called for every {@link P} element in the document.
     *
     * @param paragraph the {@link P} element to process.
     */
    protected abstract void onParagraph(P paragraph);

    /**
     * This method is called for every {@link Tbl} element in the document.
     *
     * @param table the {@link Tbl} element to process.
     */
    protected abstract void onTable(Tbl table);

    /**
     * This method is called for every {@link Tc} element in the document.
     *
     * @param tableCell the {@link Tc} element to process.
     */
    protected abstract void onTableCell(Tc tableCell);

    /**
     * This method is called for every {@link Tr} element in the document.
     *
     * @param tableRow the {@link Tr} element to process.
     */
    protected abstract void onTableRow(Tr tableRow);

    /**
     * This method is called for every {@link CommentRangeStart} element in the document.
     *
     * @param commentRangeStart the {@link CommentRangeStart} element to process.
     */
    protected abstract void onCommentRangeStart(CommentRangeStart commentRangeStart);

    /**
     * This method is called for every {@link CommentRangeEnd} element in the document.
     *
     * @param commentRangeEnd the {@link CommentRangeEnd} element to process.
     */
    protected abstract void onCommentRangeEnd(CommentRangeEnd commentRangeEnd);

    /**
     * This method is called for every {@link CommentReference} element in the document.
     *
     * @param commentReference the {@link CommentReference} element to process.
     */
    protected abstract void onCommentReference(CommentReference commentReference);
}