/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.difftests; import com.emc.apidocs.model.ApiClass; import com.emc.apidocs.model.ApiField; import com.emc.apidocs.model.ChangeState; import com.google.common.collect.Lists; import java.util.Collections; import java.util.List; public class ApiClassDiffTests { public static void main(String[] args) throws Exception { List<ApiField> sequenceA = Lists.newArrayList(newField("A"), newField("B"), newField("P"), newField("C")); List<ApiField> sequenceB = Lists.newArrayList(newField("A"), newField("B"), newField("E"), newField("D"), newField("C")); List<ApiField> diffList = generateMergedList(sequenceA, sequenceB); System.out.println("OUTPUT : "); for (ApiField s : diffList) { switch (s.changeState) { case NOT_CHANGED: System.out.print("- "); break; case REMOVED: System.out.print("< "); break; case ADDED: System.out.print("> "); break; } System.out.println(s.name); } ApiClass apiClass = new ApiClass(); apiClass.fields = diffList; System.out.println("CONTAINS CHANGES :" + containsChanges(apiClass)); } /** * For more information on the LCS algorithm, see http://en.wikipedia.org/wiki/Longest_common_subsequence_problem */ private static int[][] computeLcs(List<ApiField> sequenceA, List<ApiField> sequenceB) { int[][] lcs = new int[sequenceA.size() + 1][sequenceB.size() + 1]; for (int i = 0; i < sequenceA.size(); i++) { for (int j = 0; j < sequenceB.size(); j++) { if (sequenceA.get(i).compareTo(sequenceB.get(j)) == 0) { lcs[i + 1][j + 1] = lcs[i][j] + 1; } else { lcs[i + 1][j + 1] = Math.max(lcs[i][j + 1], lcs[i + 1][j]); } } } return lcs; } /** * Generates a merged list with changes */ public static List<ApiField> generateMergedList(List<ApiField> sequenceA, List<ApiField> sequenceB) { int[][] lcs = computeLcs(sequenceA, sequenceB); List<ApiField> mergedFields = Lists.newArrayList(); int aPos = sequenceA.size(); int bPos = sequenceB.size(); while (aPos > 0 || bPos > 0) { if (aPos > 0 && bPos > 0 && sequenceA.get(aPos - 1).compareTo(sequenceB.get(bPos - 1)) == 0) { ApiField field = sequenceA.get(aPos - 1); field.changeState = ChangeState.NOT_CHANGED; mergedFields.add(field); aPos--; bPos--; } else if (bPos > 0 && (aPos == 0 || lcs[aPos][bPos - 1] >= lcs[aPos - 1][bPos])) { ApiField field = sequenceB.get(bPos - 1); field.changeState = ChangeState.ADDED; mergedFields.add(field); bPos--; } else { ApiField field = sequenceA.get(aPos - 1); field.changeState = ChangeState.REMOVED; mergedFields.add(field); aPos--; } } // Backtracking generates the list from back to front, // so reverse it to get front-to-back. Collections.reverse(mergedFields); return mergedFields; } /** * @return Indicates if this class contains ANY changes (directly or within a fields type) */ public static boolean containsChanges(ApiClass apiClass) { for (ApiField field : apiClass.fields) { if (field.changeState != ChangeState.NOT_CHANGED) { return true; } } for (ApiField field : apiClass.fields) { if (!field.isPrimitive()) { boolean containsChanges = containsChanges(field.type); if (containsChanges) { return true; } } } return false; } private static ApiField newField(String name) { ApiField field = new ApiField(); field.name = name; field.primitiveType = "String"; return field; } }