com.cognifide.aet.job.common.comparators.source.diff.DiffParser.java Source code

Java tutorial

Introduction

Here is the source code for com.cognifide.aet.job.common.comparators.source.diff.DiffParser.java

Source

/**
 * Automated Exploratory Tests
 *
 * Copyright (C) 2013 Cognifide Limited
 *
 * 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 com.cognifide.aet.job.common.comparators.source.diff;

import com.google.common.base.Function;
import com.google.common.collect.Lists;

import com.cognifide.aet.job.common.comparators.source.diff.ResultDelta.TYPE;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import difflib.Delta;
import difflib.DiffUtils;
import difflib.Patch;

import static com.cognifide.aet.job.common.comparators.source.diff.DiffMatchPatch.Diff;

public class DiffParser {

    private static final String NEW_LINE = "\n";

    private static final String BR_TAG = "<br>";

    private static final Function<String, String> TRIM_FUNCTION = new Function<String, String>() {
        @Override
        public String apply(String input) {
            return StringUtils.trim(input);
        }
    };

    private static DiffMatchPatch diffMatchPatch = new DiffMatchPatch();

    /**
     * Generates deltas of changes using DiffUtils library. If {trimmed} all source lines are trimmed before
     * comparison in order to remove white spaces differences between html tags.
     *
     * @param pattern        - pattern to compare
     * @param source         - source to compare
     * @param trimmedLines   - flag if lines should be trimmed
     * @return List of Delta changes
     */
    public List<ResultDelta> generateDiffs(String pattern, String source, boolean trimmedLines) {
        List<String> patternList = Arrays.asList(StringUtils.split(pattern, NEW_LINE));
        List<String> sourceList = Arrays.asList(StringUtils.split(source, NEW_LINE));
        if (trimmedLines) {
            patternList = Lists.transform(patternList, TRIM_FUNCTION);
            sourceList = Lists.transform(sourceList, TRIM_FUNCTION);
        }
        Patch patch = DiffUtils.diff(patternList, sourceList);
        List<Delta> deltas = patch.getDeltas();
        return addFullSource(deltas, patternList, sourceList);
    }

    protected ResultDelta processDelta(Delta delta) {
        String originalLines = StringUtils.join(delta.getOriginal().getLines(), NEW_LINE);
        String revisedLines = StringUtils.join(delta.getRevised().getLines(), NEW_LINE);
        String originalChunkHtml;
        String revisedChunkHtml;

        if (Delta.TYPE.CHANGE.equals(delta.getType())) {
            LinkedList<Diff> diffList = diffMatchPatch.diff_main(originalLines, revisedLines);
            diffMatchPatch.diff_cleanupSemantic(diffList);

            LinkedList<Diff> originalDiffList = Lists.newLinkedList();
            LinkedList<Diff> revisedDiffList = Lists.newLinkedList();

            for (Diff diff : diffList) {
                switch (diff.operation) {
                case DELETE:
                    originalDiffList.add(diff);
                    break;
                case EQUAL:
                    originalDiffList.add(diff);
                    revisedDiffList.add(diff);
                    break;
                case INSERT:
                    revisedDiffList.add(diff);
                    break;
                default:
                    break;
                }
            }
            originalChunkHtml = diffMatchPatch.diff_prettyHtml(originalDiffList);
            revisedChunkHtml = diffMatchPatch.diff_prettyHtml(revisedDiffList);
        } else {
            originalChunkHtml = StringEscapeUtils.escapeHtml4(originalLines);
            revisedChunkHtml = StringEscapeUtils.escapeHtml4(revisedLines);
        }
        return buildDelta(originalChunkHtml, revisedChunkHtml, delta);
    }

    private ResultChunk getNoChangesResultChunk(List<String> linesList, int position) {
        String prettyHtml = StringEscapeUtils.escapeHtml4(StringUtils.join(linesList, NEW_LINE));
        return new ResultChunk(position, prettyHtml);
    }

    private ResultDelta buildDelta(String originalChunkHtml, String revisedChunkHtml, Delta delta) {
        String localOriginalChunkHtml = originalChunkHtml;
        String localRevisedChunkHtml = revisedChunkHtml;
        int originalLinesNo = delta.getOriginal().getLines().size();
        int revisedLinesNo = delta.getRevised().getLines().size();

        int sizeDiff = Math.abs(originalLinesNo - revisedLinesNo);

        if (sizeDiff > 0) {
            if (delta.getType().equals(Delta.TYPE.CHANGE)) {
                // I don't know why, but it have to be like that.
                sizeDiff++;
            }
            String alignment = StringUtils.repeat(BR_TAG, sizeDiff);
            if (originalLinesNo > revisedLinesNo) {
                localRevisedChunkHtml += alignment;
            } else if (originalLinesNo < revisedLinesNo) {
                localOriginalChunkHtml += alignment;
            }
        }

        int originalPosition = delta.getOriginal().getPosition();
        ResultChunk original = new ResultChunk(originalPosition, localOriginalChunkHtml);
        int revisedPosition = delta.getRevised().getPosition();
        ResultChunk revised = new ResultChunk(revisedPosition, localRevisedChunkHtml);
        return new ResultDelta(TYPE.valueOf(delta.getType().name()), original, revised);
    }

    private List<ResultDelta> addFullSource(List<Delta> deltaList, List<String> originalList,
            List<String> revisedList) {
        List<ResultDelta> deltaListWithSource = new ArrayList<>();
        int lastPositionOriginal = 0;
        int lastPositionRevised = 0;

        for (Delta delta : deltaList) {
            // original
            int sizeOriginal = delta.getOriginal().getLines().size();
            int positionOriginal = delta.getOriginal().getPosition();
            List<String> originalSubList = originalList.subList(lastPositionOriginal, positionOriginal);
            ResultChunk originalChunk = getNoChangesResultChunk(originalSubList, lastPositionOriginal);
            lastPositionOriginal = sizeOriginal + positionOriginal;
            // revised
            int sizeRevised = delta.getRevised().getLines().size();
            int positionRevised = delta.getRevised().getPosition();
            List<String> revisedSubList = revisedList.subList(lastPositionRevised, positionRevised);
            ResultChunk revisedChunk = getNoChangesResultChunk(revisedSubList, lastPositionRevised);
            lastPositionRevised = sizeRevised + positionRevised;
            ResultDelta resultDelta = new ResultDelta(TYPE.NO_CHANGE, originalChunk, revisedChunk);
            deltaListWithSource.add(resultDelta);
            deltaListWithSource.add(processDelta(delta));
        }
        if ((originalList.size() > lastPositionOriginal || revisedList.size() > lastPositionRevised)
                && !deltaList.isEmpty()) {
            ResultDelta resultDelta = prepareNoChangeResultDelta(originalList, revisedList, lastPositionOriginal,
                    lastPositionRevised);
            deltaListWithSource.add(resultDelta);
        }
        return deltaListWithSource;
    }

    private ResultDelta prepareNoChangeResultDelta(List<String> originalList, List<String> revisedList,
            int lastPositionOriginal, int lastPositionRevised) {
        int originalListSize = originalList.size();
        int revisedListSize = revisedList.size();
        List<String> originalSubList = originalList.subList(lastPositionOriginal, originalListSize);
        ResultChunk originalChunk = getNoChangesResultChunk(originalSubList, lastPositionOriginal);
        List<String> revisedSubList = revisedList.subList(lastPositionRevised, revisedListSize);
        ResultChunk revisedChunk = getNoChangesResultChunk(revisedSubList, lastPositionRevised);
        return new ResultDelta(TYPE.NO_CHANGE, originalChunk, revisedChunk);
    }
}