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

com.google.api.tools.framework.model.testing.BaselineDiffer Maven / Gradle / Ivy

There is a newer version: 0.0.8
Show newest version
/*
 * Copyright (C) 2016 Google Inc.
 *
 * 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.google.api.tools.framework.model.testing;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.FluentIterable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import name.fraser.neil.plaintext.diff_match_patch;

/**
 * A differ for {@link BaselineTestCase}, based on the commonly used diff_match_patch package,
 * running in line mode.
 *
 * See https://code.google.com/p/google-diff-match-patch.
 */
public class BaselineDiffer extends diff_match_patch {

  private static final Splitter LINE_SPLITTER = Splitter.on('\n');
  private static final Joiner LINE_JOINER = Joiner.on('\n');

  private static final int CONTEXT_SIZE = 2;

  public String diff(String text1, String text2) {
    // Runs the algorithm in line mode.
    // See https://code.google.com/p/google-diff-match-patch/wiki/LineOrWordDiffs.

    // Convert to lines-as-chars representation.
    LinesToCharsResult result = diff_linesToChars(text1, text2);
    String chars1 = getField(String.class, result, "chars1");
    String chars2 = getField(String.class, result, "chars2");
    @SuppressWarnings("unchecked")
    List lines = getField(List.class, result, "lineArray");

    // Perform diff.
    LinkedList diffs = diff_main(chars1, chars2, false);

    // Convert back and cleanup.
    diff_charsToLines(diffs, lines);
    diff_cleanupSemantic(diffs);

    List output = new ArrayList<>();
    for (Diff diff : diffs) {
      switch (diff.operation) {
        case EQUAL:
          addOutput(output, "= ", diff.text);
          break;
        case DELETE:
          addOutput(output, "< ", diff.text);
          break;
        case INSERT:
          addOutput(output, "> ", diff.text);
          break;
      }
    }
    collapseContext(output);
    return LINE_JOINER.join(output);
  }

  private static void addOutput(List output, final String prefix, String text) {
    output.addAll(FluentIterable.from(LINE_SPLITTER.split(text.trim())).transform(
        new Function() {
          @Override
          public String apply(String input) {
            return prefix + input;
          }
        }).toList());
  }

  /**
   * Collapses larger areas of equal diffs for better readability.
   */
  private static void collapseContext(List lines) {
    int i = 0;
    while (i < lines.size()) {
      int j = i;
      while (j < lines.size() && lines.get(j).startsWith("=")) {
        j++;
      }
      if (j - i > CONTEXT_SIZE) {
        int count = j - i - CONTEXT_SIZE;
        int start = i + CONTEXT_SIZE / 2;
        for (int k = 0; k < count && start < lines.size(); k++) {
          lines.remove(start);
          j--;
        }
        lines.add(start, String.format("= ... %d equal lines omitted ...", count));
        j++;
      }
      i = j + 1;
    }
  }

  /**
   * A hack which allows us to access unintentionally hidden fields in the Java version
   * of diff_match_patch so we can run it in line mode.
   *
   * 

Background: Fields in {@link name.fraser.neil.plaintext.diff_match_patch.LinesToCharsResult} * are declared as protected and can't be accessed outside of the package. This is a known issue * discussed in multiple forums. */ private static T getField(Class type, LinesToCharsResult result, String name) { try { Field field = LinesToCharsResult.class.getDeclaredField(name); if (field != null) { field.setAccessible(true); return type.cast(field.get(result)); } } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) { // fall through to throw } throw new IllegalArgumentException(String.format( "The field '%s' is unknown in '%s'. Check whether " + "`diff_match_patch` code has been updated so we don't need the hack here anymore!", name, result.getClass().getName())); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy