001/* A simple diff utility, implemented in Java. 002 003 Copyright (c) 2011-2018 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027 028package ptolemy.util; 029 030import java.io.File; 031import java.io.IOException; 032import java.net.MalformedURLException; 033import java.net.URL; 034import java.nio.charset.Charset; 035 036/** 037 * Read two files and compute the diff. 038 * 039 * <p>This file is based on <a href="http://introcs.cs.princeton.edu/96optimization/Diff.java#in_browser">http://introcs.cs.princeton.edu/96optimization/Diff.java</a>, from 2011, see 040 * <a href="http://introcs.cs.princeton.edu/96optimization/#in_browser">http://introcs.cs.princeton.edu/96optimization</a>. 041 * A current copy may be found at <a href="http://introcs.cs.princeton.edu/java/23recursion/Diff.java.html#in_browser">http://introcs.cs.princeton.edu/java/23recursion/Diff.java.html</a> 042 * 043 * 044 * @author Christopher Brooks 045 * @version $Id$ 046 * @since Ptolemy II 11.0 047 * @Pt.ProposedRating Red (cxh) 048 * @Pt.AcceptedRating Red (cxh) 049 */ 050public class Diff { 051 052 /** Return the difference between two strings. 053 * @param aString The first string to be compared. 054 * @param bString The secondString to be compared 055 * @return A string describing the difference between the two 056 * strings in a format similar to the Unix diff command. 057 */ 058 public static String diff(String aString, String bString) { 059 String systemEol = System.getProperty("line.separator"); 060 // Since a string my be loaded from a file 061 // that was saved on a different platform we must 062 // allow any valid line separator to split the string 063 String eol = "\r\n?|\n"; 064 String[] aStringSplit = aString.split(eol); 065 String[] bStringSplit = bString.split(eol); 066 int aNumberOfLines = aStringSplit.length; 067 int bNumberOfLines = bStringSplit.length; 068 069 // Find the Longest Common Subsequence (LCS). 070 int[][] lcs = new int[aNumberOfLines + 1][bNumberOfLines + 1]; 071 for (int aIndex = 1; aIndex < aNumberOfLines; aIndex++) { 072 for (int bIndex = 1; bIndex < bNumberOfLines; bIndex++) { 073 if (aStringSplit[aIndex].equals(bStringSplit[bIndex])) { 074 lcs[aIndex][bIndex] = lcs[aIndex - 1][bIndex - 1] + 1; 075 } else { 076 lcs[aIndex][bIndex] = Math.max(lcs[aIndex][bIndex - 1], 077 lcs[aIndex - 1][bIndex]); 078 } 079 } 080 } 081 082 // Traverse the LCS and append the differences. 083 StringBuffer result = new StringBuffer(); 084 int aIndex = 0; 085 int bIndex = 0; 086 while (aIndex < aNumberOfLines && bIndex < bNumberOfLines) { 087 if (aStringSplit[aIndex].equals(bStringSplit[bIndex])) { 088 aIndex++; 089 bIndex++; 090 } else if (lcs[aIndex + 1][bIndex] < lcs[aIndex][bIndex + 1]) { 091 result.append("> " + bStringSplit[bIndex++] + systemEol); 092 } else { 093 result.append("< " + aStringSplit[aIndex++] + systemEol); 094 } 095 } 096 097 // Append the remainder of the longer file. 098 while (aIndex < aNumberOfLines || bIndex < bNumberOfLines) { 099 if (aIndex == aNumberOfLines) { 100 result.append("> " + bStringSplit[bIndex++] + systemEol); 101 } else if (bIndex == bNumberOfLines) { 102 result.append("< " + aStringSplit[aIndex++] + systemEol); 103 } 104 } 105 return result.toString(); 106 } 107 108 /** Print the difference between two files. 109 * <p>Usage:</p> 110 * <pre> 111 * java -classpath $PTII ptolemy.util.test.Diff File1.txt File2.txt 112 * </pre> 113 114 * @param args An array of two elements, where 115 * the first element is the filename of the first 116 * file and the second element is the filename of 117 * the second file. 118 * @exception MalformedURLException If a file name cannot be converted 119 * into a URL. 120 * @exception IOException If a file cannot be read. 121 */ 122 public static void main(String[] args) 123 throws MalformedURLException, IOException { 124 if (args.length != 2) { 125 System.err.println("Error: number of arguments must be 2, " + "not " 126 + args.length + "."); 127 System.err.println("Usage: java -classpath $PTII " 128 + "ptolemy.util.test.Diff File1.txt File2.txt"); 129 } 130 // Read in each file 131 URL urlA = new File(args[0]).toURI().toURL(); 132 URL urlB = new File(args[1]).toURI().toURL(); 133 134 System.out.print(diff( 135 new String(FileUtilities.binaryReadURLToByteArray(urlA), 136 Charset.defaultCharset()), 137 new String(FileUtilities.binaryReadURLToByteArray(urlB), 138 Charset.defaultCharset()))); 139 } 140}