/*
JMeld is a visual diff and merge tool.
Copyright (C) 2007 Kees Kuip
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA
*/
package org.jmeld.diff;
import org.jmeld.*;
import org.jmeld.ui.text.*;
import org.jmeld.util.*;
import org.jmeld.util.file.*;
import java.util.*;
import java.nio.*;
public class JMDiff
{
// Class variables:
// Allocate a charBuffer once for performance. The charbuffer is used to
// store a 'line' without it's ignored characters.
static final private CharBuffer inputLine = CharBuffer.allocate(10000);
static final private CharBuffer outputLine = CharBuffer.allocate(10000);
// Instance variables:
private List<JMDiffAlgorithmIF> algorithms;
public JMDiff()
{
MyersDiff myersDiff;
// Timing/Memory (msec/Mb):
// Myers Eclipse GNU Hunt
// ================================================================================
// 2 Totally different files (116448 lines) 31317 1510 340 195
// 2 Totally different files (232896 lines) 170673 212 788 354
// 2 Medium different files (1778583 lines) 41 55 140 24679
// 2 Medium different files (10673406 lines) 216 922 632 >300000
// 2 Equal files (1778583 lines) 32 55 133 24632
// 2 Equal files (10673406 lines) 121 227 581 >60000
myersDiff = new MyersDiff();
myersDiff.checkMaxTime(true);
// MyersDiff is the fastest but can be very slow when 2 files
// are very different.
algorithms = new ArrayList<JMDiffAlgorithmIF>();
//algorithms.add(myersDiff);
// EclipseDiff looks like Myersdiff but is slower.
// It performs much better if the files are totally different
algorithms.add(new EclipseDiff());
// HuntDiff (from netbeans) is very, very slow
//algorithms.add(new HuntDiff());
}
public JMRevision diff(List<String> a, List<String> b, Ignore ignore)
throws JMeldException
{
if (a == null)
{
a = Collections.emptyList();
}
if (b == null)
{
b = Collections.emptyList();
}
return diff(a.toArray(), b.toArray(), ignore);
}
public JMRevision diff(Object[] a, Object[] b, Ignore ignore)
throws JMeldException
{
JMRevision revision;
StopWatch sp;
boolean filtered;
Object[] org;
Object[] rev;
long filteredTime;
org = a;
rev = b;
if (org == null)
{
org = new Object[] {};
}
if (rev == null)
{
rev = new Object[] {};
}
filtered = org instanceof AbstractBufferDocument.Line[]
&& rev instanceof AbstractBufferDocument.Line[];
sp = new StopWatch();
sp.start();
if (filtered)
{
org = filter(ignore, org);
rev = filter(ignore, rev);
}
filteredTime = sp.getElapsedTime();
for (JMDiffAlgorithmIF algorithm : algorithms)
{
try
{
revision = algorithm.diff(org, rev);
revision.setIgnore(ignore);
revision.update(a, b);
// revision.filter();
if (filtered)
{
adjustRevision(revision, a, (JMString[]) org, b, (JMString[]) rev);
}
if (a.length > 1000)
{
System.out.println("diff took " + sp.getElapsedTime()
+ " msec. [filter=" + filteredTime + " msec]["
+ algorithm.getClass() + "]");
}
return revision;
}
catch (JMeldException ex)
{
if (ex.getCause() instanceof MaxTimeExceededException)
{
System.out.println("Time exceeded for " + algorithm.getClass()
+ ": try next algorithm");
}
else
{
throw ex;
}
}
}
return null;
}
private void adjustRevision(JMRevision revision, Object[] orgArray,
JMString[] orgArrayFiltered, Object[] revArray,
JMString[] revArrayFiltered)
{
JMChunk chunk;
int anchor;
int size;
int index;
for (JMDelta delta : revision.getDeltas())
{
chunk = delta.getOriginal();
//System.out.print(" original=" + chunk);
index = chunk.getAnchor();
if (index < orgArrayFiltered.length)
{
anchor = orgArrayFiltered[index].lineNumber;
}
else
{
anchor = orgArray.length;
}
size = chunk.getSize();
if (size > 0)
{
index += chunk.getSize() - 1;
if (index < orgArrayFiltered.length)
{
size = orgArrayFiltered[index].lineNumber - anchor + 1;
}
/*
index += chunk.getSize();
if (index < orgArrayFiltered.length)
{
size = orgArrayFiltered[index].lineNumber - anchor;
}
*/
}
chunk.setAnchor(anchor);
chunk.setSize(size);
//System.out.println(" => " + chunk);
chunk = delta.getRevised();
//System.out.print(" revised=" + chunk);
index = chunk.getAnchor();
if (index < revArrayFiltered.length)
{
//System.out.print(" [index=" + index + ", text="
//+ revArrayFiltered[index].s + "]");
anchor = revArrayFiltered[index].lineNumber;
}
else
{
anchor = revArray.length;
}
size = chunk.getSize();
if (size > 0)
{
index += chunk.getSize() - 1;
if (index < revArrayFiltered.length)
{
size = revArrayFiltered[index].lineNumber - anchor + 1;
}
/*
index += chunk.getSize();
if (index < revArrayFiltered.length)
{
size = revArrayFiltered[index].lineNumber - anchor;
}
*/
}
chunk.setAnchor(anchor);
chunk.setSize(size);
//System.out.println(" => " + chunk);
}
}
private JMString[] filter(Ignore ignore, Object[] array)
{
List<JMString> result;
JMString jms;
int lineNumber;
synchronized (inputLine)
{
//System.out.println("> start");
result = new ArrayList<JMString>(array.length);
lineNumber = -1;
for (Object o : array)
{
lineNumber++;
inputLine.clear();
inputLine.put(o.toString());
CompareUtil.removeIgnoredChars(inputLine, ignore, outputLine);
if (outputLine.remaining() == 0)
{
continue;
}
jms = new JMString();
jms.s = outputLine.toString();
jms.lineNumber = lineNumber;
result.add(jms);
//System.out.println(" " + jms);
}
}
return result.toArray(new JMString[result.size()]);
}
class JMString
{
String s;
int lineNumber;
@Override
public int hashCode()
{
return s.hashCode();
}
@Override
public boolean equals(Object o)
{
return s.equals(((JMString) o).s);
}
@Override
public String toString()
{
return "[" + lineNumber + "] " + s;
}
}
}