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

com.github.ferstl.maven.pomenforcers.util.SideBySideDiffUtil Maven / Gradle / Ivy

Go to download

The Pedantic POM Enforcers consist of serveral Maven enforcer rules that help you keep your project setup consistent and organized.

There is a newer version: 2.2.0
Show newest version
/*
 * Copyright (c) 2012 - 2015 by Stefan Ferstl 
 *
 * 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.github.ferstl.maven.pomenforcers.util;

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

import com.google.common.base.Strings;

import difflib.Chunk;
import difflib.Delta;
import difflib.DiffUtils;


public final class SideBySideDiffUtil {

  public static String diff(Collection actual, Collection required) {
    return diff(actual, required, "", "");
  }

  public static String diff(Collection actual, Collection required, String leftTitle, String rightTitle) {

    SideBySideContext context = new SideBySideContext(actual, required, leftTitle, rightTitle);
    int offset = 0;

    for (Delta delta : context.deltas) {
      Chunk original = delta.getOriginal();
      Chunk revised = delta.getRevised();
      int currentPosition = original.getPosition() + offset;

      switch(delta.getType()) {
        case INSERT:
          offset += context.expand(currentPosition, revised.size());
          context.setRightContent(currentPosition, revised.getLines());
          break;

        case CHANGE:
          int difference = revised.size() - original.size();
          if (difference > 0) {
            offset += context.expand(currentPosition + original.size(), difference);
          } else {
            context.clearRightContent(currentPosition + revised.size(), Math.abs(difference));
          }

          context.setLeftContent(currentPosition, original.getLines());
          context.setRightContent(currentPosition, revised.getLines());
          break;

        case DELETE:
          context.setLeftContent(currentPosition, original.getLines());
          context.clearRightContent(currentPosition, original.size());
          break;

        default:
          throw new IllegalStateException("Unsupported delta type: " + delta.getType());

      }
    }

    return context.toString();
  }

  private SideBySideDiffUtil() {
    throw new AssertionError("not instantiable");
  }

  /**
   * Context to manipulate both sides of the diff.
   */
  private static class SideBySideContext {
    private static final String SIDE_SEPARATOR = " |";
    private static final String EMPTY_MARKER = "  ";
    private static final String DELETION_MARKER = "- ";
    private static final String INSERTION_MARKER = "+ ";

    private final String leftTitle;
    private final String rightTitle;
    private final int leftWidth;
    private final int rightWidth; // Only used when titles are present
    private final Collection> deltas;
    private final List left;
    private final List right;

    public SideBySideContext(Collection original, Collection revised, String leftTitle, String rightTitle) {
      this.leftTitle = leftTitle;
      this.rightTitle = rightTitle;
      List originalList = original instanceof List ? (List) original : new ArrayList<>(original);
      List revisedList = revised instanceof List ? (List) revised : new ArrayList<>(revised);

      this.deltas = DiffUtils.diff(originalList, revisedList).getDeltas();
      this.leftWidth = Math.max(getMaxWidth(original) + 2, leftTitle.length()); // +2: include the markers
      this.rightWidth = Math.max(getMaxWidth(revised) + 2, rightTitle.length()); // +2: include the markers

      int length = Math.max(original.size(), revised.size());
      length += getExpansionLength(this.deltas);
      this.left = new ArrayList<>(length);
      this.right = new ArrayList<>(length);

      for (String string : original) {
        String line = formatLine(EMPTY_MARKER, string);
        this.left.add(line);
        this.right.add(line);
      }
    }

    public void setLeftContent(int index, Collection content) {
      setContent(this.left, index, DELETION_MARKER, content);
    }

    public void setRightContent(int index, Collection content) {
      setContent(this.right, index, INSERTION_MARKER, content);
    }

    public void clearRightContent(int index, int size) {
      clearContent(this.right, index, size);
    }

    @Override
    public String toString() {
      StringBuilder sb = new StringBuilder();

      if (shouldPrintTitle()) {
        String leftTitlePadded = Strings.padEnd(this.leftTitle, this.leftWidth, ' ');
        sb.append(leftTitlePadded).append(SIDE_SEPARATOR).append(" ").append(this.rightTitle).append("\n");
        sb.append(Strings.repeat("-", this.leftWidth + SIDE_SEPARATOR.length() + this.rightWidth + 1)).append("\n");
      }

      for(int i = 0; i < this.left.size(); i++) {
        String leftLine = this.left.get(i);
        String rightLine = this.right.get(i);

        String leftPadded = Strings.padEnd(leftLine, this.leftWidth, ' ');

        sb.append(leftPadded).append(SIDE_SEPARATOR);
        if (!rightLine.isEmpty()) {
          sb.append(" ").append(rightLine);
        }
        sb.append("\n");
      }

      // Remove last newline
      if (sb.length() > 0) {
        sb.deleteCharAt(sb.length() - 1);
      }
      return sb.toString();
    }

    public boolean shouldPrintTitle() {
      return !(this.leftTitle.isEmpty() && this.rightTitle.isEmpty()) && !(this.left.isEmpty() && this.right.isEmpty());
    }

    private static int getMaxWidth(Collection content) {
      int width = 0;
      for (String string : content) {
        width = Math.max(width, string.length());
      }

      return width;
    }

    private static int getExpansionLength(Collection> deltas) {
      int length = 0;
      for(Delta delta : deltas) {
        switch(delta.getType()) {
          case INSERT:
          case CHANGE:
            int expansion = delta.getRevised().size() - delta.getOriginal().size();
            length += (expansion > 0) ? expansion : 0;
            break;

          default: // NOP
        }
      }

      return length;
    }

    private String formatLine(String prefix, String line) {
      return prefix + line;
    }

    private int expand(int index, int size) {
      String[] emptyLines = new String[size];
      Arrays.fill(emptyLines, "");
      List emptyLinesAsList = Arrays.asList(emptyLines);

      this.left.addAll(index, emptyLinesAsList);
      this.right.addAll(index, emptyLinesAsList);

      return size;
    }

    private void clearContent(List l, int index, int size) {
      if (size < 1) {
        return;
      }

      for(int i = 0; i < size; i++) {
        l.set(i + index, "");
      }
    }

    private void setContent(List l, int index, String prefix, Collection lines) {
      int i = 0;

      for (String line : lines) {
        l.set(index + i++, formatLine(prefix, line));
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy