All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cdc.office.tools.KeyedTableDiffExporter Maven / Gradle / Ivy

The newest version!
package cdc.office.tools;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Workbook;
import org.odftoolkit.odfdom.doc.OdfSpreadsheetDocument;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeStyles;
import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;

import cdc.office.ss.WorkbookKind;
import cdc.office.ss.WorkbookWriter;
import cdc.office.ss.WorkbookWriterFeatures;
import cdc.office.ss.WorkbookWriterHelper;
import cdc.office.ss.csv.CsvWorkbookWriter;
import cdc.office.ss.excel.ExcelWorkbookWriter;
import cdc.office.ss.odf.OdsWorkbookWriter;
import cdc.office.tables.Header;
import cdc.office.tables.TableSection;
import cdc.office.tables.diff.CellDiff;
import cdc.office.tables.diff.CellDiffKind;
import cdc.office.tables.diff.KeyedTableDiff;
import cdc.office.tables.diff.KeyedTableDiff.Synthesis.Action;
import cdc.office.tables.diff.LocalizedCellDiff;
import cdc.office.tables.diff.RowDiff;
import cdc.office.tables.diff.RowDiffKind;
import cdc.office.tables.diff.Side;
import cdc.tuples.CTupleN;
import cdc.tuples.TupleN;
import cdc.util.encoding.ExtensionEncoder;
import cdc.util.lang.Checks;
import cdc.util.lang.UnexpectedValueException;
import cdc.util.strings.StringComparison;

/**
 * Class used to export a KeyedTableDiff to an Office file.
 *
 * @author Damien Carbonne
 */
public final class KeyedTableDiffExporter {
    public static final String DEFAULT_ADDED_MARK = "";
    public static final String DEFAULT_REMOVED_MARK = "";
    public static final String DEFAULT_CHANGED_MARK = "";
    public static final String DEFAULT_UNCHANGED_MARK = "";
    public static final String DEFAULT_FILE1_MARK = "1";
    public static final String DEFAULT_FILE2_MARK = "2";
    public static final String DEFAULT_DIFF_MARK = "Diff";
    public static final String DEFAULT_DIFF_SHEET_NAME = "Delta";
    public static final String DEFAULT_SYNTHESIS_SHEET_NAME = "Synthesis";

    private String file1Mark;
    private String file2Mark;
    private String diffMark;
    private String changedMark;
    private String addedMark;
    private String removedMark;
    private String unchangedMark;
    private String diffSheetName = DEFAULT_DIFF_SHEET_NAME;
    private String synthesisSheetName;
    private WorkbookWriterFeatures features;
    private int maxRowsPerSheet;
    private Header header1;
    private Header header2;
    private final ExtensionEncoder encoder1 = new ExtensionEncoder<>(String.class, String.class);
    private final ExtensionEncoder encoder2 = new ExtensionEncoder<>(String.class, String.class);
    private final Set hints = EnumSet.noneOf(Hint.class);

    public enum Hint {
        /** If set, one column describing the change of each line is added. */
        ADD_LINE_DIFF_COLUMN,
        /** If set, one column describing the change of each cell is added for each data column. */
        ADD_CELL_DIFF_COLUMNS,
        /** If set, the synthesis sheet is generated. */
        SAVE_SYNTHESIS,
        /** If set, in each changed cell, both values are displayed. */
        SHOW_CHANGE_DETAILS,
        /** If set, colors are used with formats that supports them. */
        SHOW_COLORS,
        /** If set, lines are sorted using key columns. */
        SORT_LINES,
        /** If set, text marks are used to show changes. */
        SHOW_MARKS,
        /** If set, original ,names are displayed in headers. Useful when mapping is used. */
        SHOW_ORIGINAL_NAMES,
        /** If set, lines that are identical are displayed. */
        SHOW_UNCHANGED_LINES,
        /** If set, 2 columns are displayed for each input column: one first data 1, one for data 2 */
        SPLIT_COMPARISONS,
        /** If set, indexing is disabled. Should be used when one knows that result holds on 1 sheet. */
        NO_INDEXING
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter() {
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setHeader1(Header header1) {
        this.header1 = header1;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setHeader2(Header header2) {
        this.header2 = header2;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setMap1(Map map1) {
        for (final Map.Entry entry : map1.entrySet()) {
            this.encoder1.put(entry.getKey(), entry.getValue());
        }
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setMap2(Map map2) {
        for (final Map.Entry entry : map2.entrySet()) {
            this.encoder2.put(entry.getKey(), entry.getValue());
        }
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setFile1Mark(String file1Mark) {
        this.file1Mark = file1Mark;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setFile2Mark(String file2Mark) {
        this.file2Mark = file2Mark;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setDiffMark(String diffMark) {
        this.diffMark = diffMark;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setChangedMark(String changedMark) {
        this.changedMark = changedMark;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setAddedMark(String addedMark) {
        this.addedMark = addedMark;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setRemovedMark(String removedMark) {
        this.removedMark = removedMark;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setUnchangedMark(String unchangedMark) {
        this.unchangedMark = unchangedMark;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setFeatures(WorkbookWriterFeatures features) {
        this.features = WorkbookWriterFeatures.builder()
                                              .set(features)
                                              .enable(WorkbookWriterFeatures.Feature.NO_CELL_STYLES)
                                              .build();
        return this;
    }

    private KeyedTableDiffExporter hint(Hint hint,
                                        boolean enabled) {
        if (enabled) {
            hints.add(hint);
        } else {
            hints.remove(hint);
        }
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setSortLines(boolean sortLines) {
        return hint(Hint.SORT_LINES, sortLines);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setShowUnchangedLines(boolean showUnchangedLines) {
        return hint(Hint.SHOW_UNCHANGED_LINES, showUnchangedLines);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setShowColors(boolean showColors) {
        return hint(Hint.SHOW_COLORS, showColors);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setShowMarks(boolean showMarks) {
        return hint(Hint.SHOW_MARKS, showMarks);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setShowChangeDetails(boolean showChangeDetails) {
        return hint(Hint.SHOW_CHANGE_DETAILS, showChangeDetails);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setSheetName(String sheetName) {
        this.diffSheetName = sheetName;
        return this;
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setSaveSynthesis(boolean saveSynthesis) {
        return hint(Hint.SAVE_SYNTHESIS, saveSynthesis);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setAddLineDiffColumn(boolean addLineDiffColumn) {
        return hint(Hint.ADD_LINE_DIFF_COLUMN, addLineDiffColumn);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setAddCellDiffColumns(boolean addCellDiffColumns) {
        return hint(Hint.ADD_CELL_DIFF_COLUMNS, addCellDiffColumns);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setShowOriginalNames(boolean showOriginalNames) {
        return hint(Hint.SHOW_ORIGINAL_NAMES, showOriginalNames);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setSplitComparisons(boolean splitComparisons) {
        return hint(Hint.SPLIT_COMPARISONS, splitComparisons);
    }

    @Deprecated(since = "2024-06-02", forRemoval = true)
    public KeyedTableDiffExporter setMaxRowsPerSheet(int maxRowsPerSheet) {
        this.maxRowsPerSheet = maxRowsPerSheet;
        return this;
    }

    private KeyedTableDiffExporter(Builder builder) {
        this.file1Mark = Checks.isNotNull(builder.file1Mark, "file1Mark");
        this.file2Mark = Checks.isNotNull(builder.file2Mark, "file2Mark");
        this.diffMark = Checks.isNotNull(builder.diffMark, "diffMark");
        this.changedMark = Checks.isNotNull(builder.changedMark, "changeMark");
        this.addedMark = Checks.isNotNull(builder.addedMark, "addedMark");
        this.removedMark = Checks.isNotNull(builder.removedMark, "removedMark");
        this.unchangedMark = Checks.isNotNull(builder.unchangedMark, "unchangedMark");
        this.diffSheetName = Checks.isNotNull(builder.diffSheetName, "diffSheetName");
        this.synthesisSheetName = Checks.isNotNull(builder.synthesisSheetName, "synthesisSheetName");
        this.features = WorkbookWriterFeatures.builder()
                                              .set(builder.features)
                                              .enable(WorkbookWriterFeatures.Feature.NO_CELL_STYLES)
                                              .build();
        this.maxRowsPerSheet = builder.maxRowsPerSheet;
        this.header1 = Checks.isNotNull(builder.header1, "header1");
        this.header2 = Checks.isNotNull(builder.header2, "header2");
        this.hints.addAll(builder.hints);
        for (final Map.Entry entry : builder.map1.entrySet()) {
            this.encoder1.put(entry.getKey(), entry.getValue());
        }
        for (final Map.Entry entry : builder.map2.entrySet()) {
            this.encoder2.put(entry.getKey(), entry.getValue());
        }
    }

    private String getOriginalName(String name,
                                   Side side) {
        if (side == Side.LEFT) {
            final String s = encoder1.decode(name);
            if (s == null) {
                if (header1.contains(name)) {
                    return name;
                } else {
                    return "?";
                }
            } else {
                return s;
            }
        } else {
            final String s = encoder2.decode(name);
            if (s == null) {
                if (header2.contains(name)) {
                    return name;
                } else {
                    return "?";
                }
            } else {
                return s;
            }
        }
    }

    public void save(KeyedTableDiff diff,
                     File file) throws IOException {
        final WorkbookKind outputKind = WorkbookKind.from(file);

        if (outputKind == null) {
            throw new IOException("Unrecognized output format for " + file);
        }

        switch (outputKind) {
        case CSV -> new CsvGenerator().generate(file, diff);
        case XLS, XLSX, XLSM -> new ExcelGenerator().generate(file, diff);
        case ODS -> new OdsGenerator().generate(file, diff);
        default -> throw new UnexpectedValueException(outputKind);
        }
    }

    /**
     * Adds a synthesis sheet to a workbook.
     *
     * @param synthesis The synthesis.
     * @param writer The writer.
     * @throws IOException When an IO error occurs.
     */
    public void addSynthesisSheet(KeyedTableDiff.Synthesis synthesis,
                                  WorkbookWriter writer) throws IOException {
        final boolean hasDuplicates = synthesis.getLinesCount(Action.DUPLICATE1) > 0
                || synthesis.getLinesCount(Action.DUPLICATE2) > 0;
        final String NA = "N/A";

        writer.beginSheet(synthesisSheetName);
        writer.beginRow(TableSection.HEADER);
        writer.addCells("Item",
                        Action.ADDED,
                        Action.REMOVED,
                        Action.CHANGED,
                        Action.SAME);
        if (hasDuplicates) {
            writer.addCells(
                            Action.DUPLICATE1,
                            Action.DUPLICATE2);
        }

        writer.beginRow(TableSection.DATA);
        writer.addCell("Lines");
        writer.addCell(synthesis.getLinesCount(Action.ADDED));
        writer.addCell(synthesis.getLinesCount(Action.REMOVED));
        writer.addCell(synthesis.getLinesCount(Action.CHANGED));
        writer.addCell(synthesis.getLinesCount(Action.SAME));
        if (hasDuplicates) {
            writer.addCell(synthesis.getLinesCount(Action.DUPLICATE1));
            writer.addCell(synthesis.getLinesCount(Action.DUPLICATE2));
        }

        writer.beginRow(TableSection.DATA);
        writer.addCell("Cells");
        writer.addCell(synthesis.getCellsCount(Action.ADDED));
        writer.addCell(synthesis.getCellsCount(Action.REMOVED));
        writer.addCell(synthesis.getCellsCount(Action.CHANGED));
        writer.addCell(synthesis.getCellsCount(Action.SAME));
        if (hasDuplicates) {
            writer.addCell(NA);
            writer.addCell(NA);
        }

        for (final String name : synthesis.getColumnNames()) {
            writer.beginRow(TableSection.DATA);
            if (hints.contains(Hint.SHOW_ORIGINAL_NAMES)) {
                writer.addCell(name + " (" + getOriginalName(name, Side.LEFT) + "/" + getOriginalName(name, Side.RIGHT) + ")");
            } else {
                writer.addCell(name);
            }
            writer.addCell(synthesis.getColumnCellsCount(name, Action.ADDED));
            writer.addCell(synthesis.getColumnCellsCount(name, Action.REMOVED));
            writer.addCell(synthesis.getColumnCellsCount(name, Action.CHANGED));
            writer.addCell(synthesis.getColumnCellsCount(name, Action.SAME));
            if (hasDuplicates) {
                writer.addCell(NA);
                writer.addCell(NA);
            }
        }
    }

    /**
     * Adds diff sheets to a workbook.
     *
     * @param diff The differences
     * @param writer The writer.
     * @throws IOException When an IO error occurs.
     */
    public void addDiffSheets(KeyedTableDiff diff,
                              WorkbookWriter writer) throws IOException {
        if (writer instanceof final CsvWorkbookWriter w) {
            new CsvGenerator().generate(w, diff);
        } else if (writer instanceof final ExcelWorkbookWriter w) {
            new ExcelGenerator().generate(w, diff);
        } else if (writer instanceof final OdsWorkbookWriter w) {
            new OdsGenerator().generate(w, diff);
        } else {
            throw new IllegalArgumentException("Unsupported writer " + writer);
        }
    }

    /**
     * Base generator class.
     *
     * @author Damien Carbonne
     *
     * @param  The WorkbookWriter type.
     */
    private abstract class AbstractGenerator> {
        private String wrap(String s) {
            return s == null ? "" : s;
        }

        private String getEffectiveMark(CellDiffKind kind) {
            if (hints.contains(Hint.SHOW_MARKS)) {
                switch (kind) {
                case ADDED:
                    return addedMark;
                case CHANGED:
                    return changedMark;
                case REMOVED:
                    return removedMark;
                case NULL, SAME:
                    return unchangedMark;
                default:
                    throw new UnexpectedValueException(kind);
                }
            } else {
                return "";
            }
        }

        private void generateHeader(W writer,
                                    Header header) throws IOException {
            // Header
            writer.beginRow(TableSection.HEADER);
            // Add the line diff column
            if (hints.contains(Hint.ADD_LINE_DIFF_COLUMN)) {
                addHeaderCell(writer, wrap(diffMark));
            }
            // Add other columns
            for (final String name : header.getSortedNames()) {
                if (hints.contains(Hint.ADD_CELL_DIFF_COLUMNS)) {
                    addHeaderCell(writer, name + " " + diffMark);
                }
                if (hints.contains(Hint.SPLIT_COMPARISONS)) {
                    if (hints.contains(Hint.SHOW_ORIGINAL_NAMES)) {
                        addHeaderCell(writer, name + " " + file1Mark + " " + getOriginalName(name, Side.LEFT));
                        addHeaderCell(writer, name + " " + file2Mark + " " + getOriginalName(name, Side.RIGHT));
                    } else {
                        addHeaderCell(writer, name + " " + file1Mark);
                        addHeaderCell(writer, name + " " + file2Mark);
                    }
                } else {
                    if (hints.contains(Hint.SHOW_ORIGINAL_NAMES)) {
                        addHeaderCell(writer,
                                      name + " " + getOriginalName(name, Side.LEFT) + " " + getOriginalName(name, Side.RIGHT));
                    } else {
                        addHeaderCell(writer, name);
                    }
                }
            }
        }

        protected final void generate(W writer,
                                      KeyedTableDiff diff) throws IOException {
            initWorkbook(writer);

            final WorkbookWriterHelper helper = new WorkbookWriterHelper<>(writer, !hints.contains(Hint.NO_INDEXING));
            if (hints.contains(Hint.SAVE_SYNTHESIS)) {
                addSynthesisSheet(diff.getSynthesis(), writer);
            }

            // Initial diff header
            // It does not take into account options that can add new columns
            final Header header = diff.getDiffHeader();

            if (hints.contains(Hint.NO_INDEXING)) {
                helper.beginSheets(diffSheetName, "", w -> generateHeader(w, header), maxRowsPerSheet);
            } else {
                helper.beginSheets(diffSheetName + "#", "", w -> generateHeader(w, header), maxRowsPerSheet);
            }

            // Data
            final List> keys = diff.getKeys();
            if (hints.contains(Hint.SORT_LINES)) {
                final Comparator> comparator = TupleN.comparator(StringComparison::compareDecimalDigits);
                Collections.sort(keys, comparator);
            }

            for (final CTupleN key : keys) {
                final RowDiff rdiff = diff.getDiff(key);
                if (rdiff.getKind() != RowDiffKind.SAME || hints.contains(Hint.SHOW_UNCHANGED_LINES)) {
                    helper.beginRow(TableSection.DATA);
                    if (hints.contains(Hint.ADD_LINE_DIFF_COLUMN)) {
                        addMarkCell(writer, rdiff.getKind());
                    }
                    for (final LocalizedCellDiff lcdiff : rdiff.getDiffs()) {
                        final CellDiff cdiff = lcdiff.getDiff();

                        if (hints.contains(Hint.ADD_CELL_DIFF_COLUMNS)) {
                            addMarkCell(writer, cdiff.getKind());
                        }

                        switch (cdiff.getKind()) {
                        case ADDED:
                            if (hints.contains(Hint.SPLIT_COMPARISONS)) {
                                addEmptyCell(writer);
                            }
                            addSingleValueCell(writer,
                                               cdiff.getKind(),
                                               getEffectiveMark(cdiff.getKind()) + wrap(cdiff.getRight()));
                            break;

                        case SAME, NULL:
                            if (hints.contains(Hint.SPLIT_COMPARISONS)) {
                                addSingleValueCell(writer,
                                                   cdiff.getKind(),
                                                   getEffectiveMark(cdiff.getKind()) + wrap(cdiff.getLeft()));
                            }
                            addSingleValueCell(writer,
                                               cdiff.getKind(),
                                               getEffectiveMark(cdiff.getKind()) + wrap(cdiff.getRight()));
                            break;

                        case CHANGED:
                            if (hints.contains(Hint.SPLIT_COMPARISONS)) {
                                addSingleValueCell(writer,
                                                   CellDiffKind.REMOVED,
                                                   getEffectiveMark(CellDiffKind.REMOVED) + wrap(cdiff.getLeft()));
                                addSingleValueCell(writer,
                                                   CellDiffKind.ADDED,
                                                   getEffectiveMark(CellDiffKind.ADDED) + wrap(cdiff.getRight()));
                            } else {
                                if (hints.contains(Hint.SHOW_CHANGE_DETAILS)) {
                                    addDualValueCell(writer,
                                                     getEffectiveMark(CellDiffKind.REMOVED) + wrap(cdiff.getLeft()),
                                                     getEffectiveMark(CellDiffKind.ADDED) + wrap(cdiff.getRight()));
                                } else {
                                    addSingleValueCell(writer,
                                                       CellDiffKind.CHANGED,
                                                       getEffectiveMark(CellDiffKind.CHANGED) + wrap(cdiff.getRight()));
                                }
                            }
                            break;

                        case REMOVED:
                            addSingleValueCell(writer,
                                               cdiff.getKind(),
                                               getEffectiveMark(cdiff.getKind()) + wrap(cdiff.getLeft()));
                            if (hints.contains(Hint.SPLIT_COMPARISONS)) {
                                addEmptyCell(writer);
                            }
                            break;

                        default:
                            throw new UnexpectedValueException(cdiff.getKind());
                        }
                    }
                }
            }
        }

        protected final void generate(File file,
                                      KeyedTableDiff diff) throws IOException {
            try (final W writer = createWorkbook(file)) {
                generate(writer, diff);
            }
        }

        protected abstract W createWorkbook(File file) throws IOException;

        protected abstract void initWorkbook(W writer) throws IOException;

        protected abstract void addHeaderCell(W writer,
                                              String name) throws IOException;

        protected abstract void addMarkCell(W writer,
                                            RowDiffKind kind) throws IOException;

        protected abstract void addMarkCell(W writer,
                                            CellDiffKind kind) throws IOException;

        protected abstract void addEmptyCell(W writer) throws IOException;

        protected abstract void addSingleValueCell(W writer,
                                                   CellDiffKind kind,
                                                   String value) throws IOException;

        protected abstract void addDualValueCell(W writer,
                                                 String value1,
                                                 String value2) throws IOException;
    }

    /**
     * Csv Generator.
     *
     * @author Damien Carbonne
     */
    private final class CsvGenerator extends AbstractGenerator {
        public CsvGenerator() {
            super();
        }

        @Override
        protected CsvWorkbookWriter createWorkbook(File file) throws IOException {
            return new CsvWorkbookWriter(file, features);
        }

        @Override
        protected void initWorkbook(CsvWorkbookWriter writer) throws IOException {
            // Ignore
        }

        @Override
        protected void addHeaderCell(CsvWorkbookWriter writer,
                                     String name) throws IOException {
            writer.addCell(name);
        }

        @Override
        protected void addMarkCell(CsvWorkbookWriter writer,
                                   RowDiffKind kind) throws IOException {
            writer.addCell(kind);
        }

        @Override
        protected void addMarkCell(CsvWorkbookWriter writer,
                                   CellDiffKind kind) throws IOException {
            writer.addCell(kind);
        }

        @Override
        protected void addEmptyCell(CsvWorkbookWriter writer) throws IOException {
            writer.addEmptyCell();
        }

        @Override
        protected void addSingleValueCell(CsvWorkbookWriter writer,
                                          CellDiffKind kind,
                                          String value) throws IOException {
            writer.addCell(value);
        }

        @Override
        protected void addDualValueCell(CsvWorkbookWriter writer,
                                        String value1,
                                        String value2) throws IOException {
            writer.addCell(value1 + "\n" + value2);
        }
    }

    /**
     * Excel Generator.
     *
     * @author Damien Carbonne
     */
    private final class ExcelGenerator extends AbstractGenerator {
        private CellStyle addedStyle;
        private CellStyle removedStyle;
        private CellStyle changedStyle;
        private CellStyle unchangedStyle;
        private CellStyle headerStyle;
        private Font removedFont;
        private Font addedFont;

        public ExcelGenerator() {
            super();
        }

        private void createStyles(Workbook workbook) {
            if (hints.contains(Hint.SHOW_COLORS)) {
                addedStyle = createStyle(workbook, IndexedColors.BLUE);
                removedStyle = createStyle(workbook, IndexedColors.RED);
                changedStyle = createStyle(workbook, IndexedColors.PINK);
                unchangedStyle = createStyle(workbook, IndexedColors.BLACK);
                removedFont = createFont(workbook, IndexedColors.RED);
                addedFont = createFont(workbook, IndexedColors.BLUE);
            } else {
                addedStyle = null;
                removedStyle = null;
                changedStyle = null;
                unchangedStyle = null;
                removedFont = null;
                addedFont = null;
            }
            headerStyle = createStyle(workbook, IndexedColors.BLACK);

        }

        private CellStyle getStyle(CellDiffKind kind) {
            switch (kind) {
            case ADDED:
                return addedStyle;
            case CHANGED:
                return changedStyle;
            case REMOVED:
                return removedStyle;
            case SAME, NULL:
                return unchangedStyle;
            default:
                throw new UnexpectedValueException(kind);
            }
        }

        private CellStyle getStyle(RowDiffKind kind) {
            switch (kind) {
            case ADDED:
                return addedStyle;
            case CHANGED:
                return changedStyle;
            case REMOVED:
                return removedStyle;
            case SAME:
                return unchangedStyle;
            default:
                throw new UnexpectedValueException(kind);
            }
        }

        @Override
        protected ExcelWorkbookWriter createWorkbook(File file) throws IOException {
            return new ExcelWorkbookWriter(file,
                                           features,
                                           true);
        }

        @Override
        protected void initWorkbook(ExcelWorkbookWriter writer) throws IOException {
            createStyles(writer.getWorkbook());
        }

        @Override
        protected void addHeaderCell(ExcelWorkbookWriter writer,
                                     String name) throws IOException {
            writer.addCell(name);
            if (hints.contains(Hint.SHOW_COLORS)) {
                writer.getCell().setCellStyle(headerStyle);
            }
        }

        @Override
        protected void addMarkCell(ExcelWorkbookWriter writer,
                                   RowDiffKind kind) throws IOException {
            writer.addCell(kind);
            if (hints.contains(Hint.SHOW_COLORS)) {
                writer.getCell().setCellStyle(getStyle(kind));
            }
        }

        @Override
        protected void addMarkCell(ExcelWorkbookWriter writer,
                                   CellDiffKind kind) throws IOException {
            writer.addCell(kind);
            if (hints.contains(Hint.SHOW_COLORS)) {
                writer.getCell().setCellStyle(getStyle(kind));
            }
        }

        @Override
        protected void addEmptyCell(ExcelWorkbookWriter writer) throws IOException {
            writer.addEmptyCell();
        }

        @Override
        protected void addSingleValueCell(ExcelWorkbookWriter writer,
                                          CellDiffKind kind,
                                          String value) throws IOException {
            writer.addCell(value);
            if (hints.contains(Hint.SHOW_COLORS)) {
                writer.getCell().setCellStyle(getStyle(kind));
            }
        }

        @Override
        protected void addDualValueCell(ExcelWorkbookWriter writer,
                                        String value1,
                                        String value2) throws IOException {
            if (hints.contains(Hint.SHOW_COLORS)) {
                // Set default style to removed and set font for added.
                // Otherwise, it seems some issues may arise with large files.
                final int leftLength = value1.length();
                final String s = value1 + "\n" + value2;
                final RichTextString text = writer.getWorkbook().getCreationHelper().createRichTextString(s);
                // text.applyFont(0, leftLength, removedFont);
                text.applyFont(leftLength, s.length(), addedFont);
                writer.addCell("");
                writer.getCell().setCellStyle(removedStyle);
                writer.getCell().setCellValue(text);
            } else {
                writer.addCell(value1 + "\n" + value2);
            }
        }
    }

    /**
     * Ods Generator.
     *
     * @author Damien Carbonne
     */
    private final class OdsGenerator extends AbstractGenerator {
        public OdsGenerator() {
            super();
        }

        private void createStyle(OdfSpreadsheetDocument doc) {
            final OdfOfficeStyles styles = doc.getOrCreateDocumentStyles();
            final OdfStyle style = styles.newStyle("xxx", OdfStyleFamily.Text);
        }

        @Override
        protected OdsWorkbookWriter createWorkbook(File file) throws IOException {
            return new OdsWorkbookWriter(file, features);
        }

        @Override
        protected void initWorkbook(OdsWorkbookWriter writer) throws IOException {
            // TODO init
        }

        @Override
        protected void addHeaderCell(OdsWorkbookWriter writer,
                                     String name) throws IOException {
            writer.addCell(name);
            // TODO style
        }

        @Override
        protected void addMarkCell(OdsWorkbookWriter writer,
                                   RowDiffKind kind) throws IOException {
            writer.addCell(kind);
            // TODO style
        }

        @Override
        protected void addMarkCell(OdsWorkbookWriter writer,
                                   CellDiffKind kind) throws IOException {
            writer.addCell(kind);
            // TODO style
        }

        @Override
        protected void addEmptyCell(OdsWorkbookWriter writer) throws IOException {
            writer.addEmptyCell();
        }

        @Override
        protected void addSingleValueCell(OdsWorkbookWriter writer,
                                          CellDiffKind kind,
                                          String value) throws IOException {
            writer.addCell(value);
            // TODO style
        }

        @Override
        protected void addDualValueCell(OdsWorkbookWriter writer,
                                        String value1,
                                        String value2) throws IOException {
            writer.addCell(value1 + "\n" + value2);
            // TODO style
        }
    }

    static CellStyle createStyle(Workbook workbook,
                                 IndexedColors color) {
        // Try to retrieve a previously created style
        // for (int index = 0; index < workbook.getNumCellStyles(); index++) {
        // final CellStyle s = workbook.getCellStyleAt(index);
        // final int i = s.getFontIndex();
        // final Font f = workbook.getFontAt(i);
        // if (f.getColor() == color.index) {
        // return s;
        // }
        // }
        final CellStyle style = workbook.createCellStyle();
        // style.setWrapText(true);
        final Font font = workbook.createFont();
        font.setColor(color.index);
        style.setFont(font);
        return style;
    }

    static Font createFont(Workbook workbook,
                           IndexedColors color) {
        final Font font = workbook.createFont();
        font.setColor(color.index);
        return font;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        private String file1Mark = DEFAULT_FILE1_MARK;
        private String file2Mark = DEFAULT_FILE2_MARK;
        private String diffMark = DEFAULT_DIFF_MARK;
        private String changedMark = DEFAULT_CHANGED_MARK;
        private String addedMark = DEFAULT_ADDED_MARK;
        private String removedMark = DEFAULT_REMOVED_MARK;
        private String unchangedMark = DEFAULT_UNCHANGED_MARK;
        private String diffSheetName = DEFAULT_DIFF_SHEET_NAME;
        private String synthesisSheetName = DEFAULT_SYNTHESIS_SHEET_NAME;
        private int maxRowsPerSheet = -1;
        private Header header1;
        private Header header2;
        private WorkbookWriterFeatures features = WorkbookWriterFeatures.STANDARD_FAST;
        private final Map map1 = new HashMap<>();
        private final Map map2 = new HashMap<>();
        private final Set hints = EnumSet.noneOf(Hint.class);

        private Builder() {
        }

        public Builder file1Mark(String file1Mark) {
            this.file1Mark = file1Mark;
            return this;
        }

        public Builder file2Mark(String file2Mark) {
            this.file2Mark = file2Mark;
            return this;
        }

        public Builder diffMark(String diffMark) {
            this.diffMark = diffMark;
            return this;
        }

        public Builder changedMark(String changedMark) {
            this.changedMark = changedMark;
            return this;
        }

        public Builder addedMark(String addedMark) {
            this.addedMark = addedMark;
            return this;
        }

        public Builder removedMark(String removedMark) {
            this.removedMark = removedMark;
            return this;
        }

        public Builder unchangedMark(String unchangedMark) {
            this.unchangedMark = unchangedMark;
            return this;
        }

        public Builder diffSheetName(String diffSheetName) {
            this.diffSheetName = diffSheetName;
            return this;
        }

        public Builder synthesisSheetName(String synthesisSheetName) {
            this.synthesisSheetName = synthesisSheetName;
            return this;
        }

        public Builder maxRowsPerSheet(int maxRowsPerSheet) {
            this.maxRowsPerSheet = maxRowsPerSheet;
            return this;
        }

        public Builder header1(Header header1) {
            this.header1 = header1;
            return this;
        }

        public Builder header2(Header header2) {
            this.header2 = header2;
            return this;
        }

        public Builder features(WorkbookWriterFeatures features) {
            this.features = features;
            return this;
        }

        public Builder map1(Map map1) {
            this.map1.putAll(map1);
            return this;
        }

        public Builder map2(Map map2) {
            this.map2.putAll(map2);
            return this;
        }

        public Builder hint(Hint hint) {
            this.hints.add(hint);
            return this;
        }

        public Builder hint(Hint hint,
                            boolean enabled) {
            if (enabled) {
                this.hints.add(hint);
            } else {
                this.hints.remove(hint);
            }
            return this;
        }

        public KeyedTableDiffExporter build() {
            return new KeyedTableDiffExporter(this);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy