    001/*
    002 * Copyright 2025 The Apache Software Foundation.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *      http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    013 * See the License for the specific language governing permissions and
    014 * limitations under the License.
    015 */
    016package org.apache.wiki.diff;
    017
    018import com.github.difflib.DiffUtils;
    019import com.github.difflib.patch.AbstractDelta;
    020import com.github.difflib.patch.Patch;
    021import java.io.IOException;
    022import java.util.List;
    023import java.util.Properties;
    024import org.apache.wiki.api.core.Context;
    025import org.apache.wiki.api.core.Engine;
    026import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
    027
    028/**
    029 * SVN/Git style diff provider. Uses the DiffLib, ASF 2.0 licensed.
    030 *
    031 * @since 3.0.0
    032 */
    033public class SvnStyleDiffProvider implements DiffProvider {
    034
    035    public static final String CSS_DIFF_ADDED = "<tr class=\"diffadd\"><td>";
    036    public static final String CSS_DIFF_REMOVED = "<tr class=\"diffrem\"><td>";
    037    public static final String CSS_DIFF_UNCHANGED = "<tr class=\"diff\"><td>";
    038    public static final String CSS_DIFF_CLOSE = "</td></tr>\n";
    039    public static final String CELL_CHANGE = "</td><td>";
    040
    041    @Override
    042    public String makeDiffHtml(Context context, String originalText, String modifiedText) {
    043
    044        List<String> original = originalText.lines().toList();
    045        List<String> modified = modifiedText.lines().toList();
    046        StringBuilder ret = new StringBuilder();
    047        ret.append("<table class=\"diff\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n");
    048
    049        Patch<String> patch = DiffUtils.diff(original, modified);
    050        int lineNumber = 1;
    051        int currentOriginalLine = 0;
    052        int currentModifiedLine = 0;
    053
    054        for (AbstractDelta<String> delta : patch.getDeltas()) {
    055            int originalPosition = delta.getSource().getPosition();
    056            int modifiedPosition = delta.getTarget().getPosition();
    057
    058            // Output unchanged lines before the delta
    059            while (currentOriginalLine < originalPosition && currentModifiedLine < modifiedPosition) {
    060                ret.append(CSS_DIFF_UNCHANGED);
    061                ret.append(lineNumber).append(CELL_CHANGE);
    062                ret.append(original.get(currentOriginalLine));
    063                ret.append(CSS_DIFF_CLOSE);
    064                //System.out.println("  " + lineNumber + " " + original.get(currentOriginalLine));
    065                lineNumber++;
    066                currentOriginalLine++;
    067                currentModifiedLine++;
    068            }
    069
    070            List<String> originalLines = delta.getSource().getLines();
    071            List<String> revisedLines = delta.getTarget().getLines();
    072
    073            for (String line : originalLines) {
    074                ret.append(CSS_DIFF_REMOVED);
    075                ret.append(lineNumber).append(CELL_CHANGE);
    076                ret.append(line);
    077                ret.append(CSS_DIFF_CLOSE);
    078                //System.out.println("- " + lineNumber + " " + line);
    079                lineNumber++;
    080                currentOriginalLine++;
    081            }
    082
    083            for (String line : revisedLines) {
    084                ret.append(CSS_DIFF_ADDED);
    085                ret.append(lineNumber).append(CELL_CHANGE);
    086                ret.append(line);
    087                ret.append(CSS_DIFF_CLOSE);
    088                //System.out.println("+ " + lineNumber + " " + line);
    089                lineNumber++;
    090                currentModifiedLine++;
    091            }
    092        }
    093
    094        // Output any remaining unchanged lines at the end
    095        while (currentOriginalLine < original.size() && currentModifiedLine < modified.size()) {
    096            ret.append(CSS_DIFF_UNCHANGED);
    097            ret.append(lineNumber).append(CELL_CHANGE);
    098            ret.append(original.get(currentOriginalLine));
    099            ret.append(CSS_DIFF_CLOSE);
    100
    101            //System.out.println("  " + lineNumber + " " + original.get(currentOriginalLine));
    102            lineNumber++;
    103            currentOriginalLine++;
    104            currentModifiedLine++;
    105        }
    106        ret.append("</table>\n");
    107        return ret.toString();
    108    }
    109
    110    @Override
    111    public void initialize(Engine engine, Properties properties) throws NoRequiredPropertyException, IOException {
    112    }
    113
    114    @Override
    115    public String getProviderInfo() {
    116        return "SvnStyleDiffProvider";
    117    }
    118
    119}