blob: e52a74ed1ad7970bdc449b425b216029996423c8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Ericsson AB and others.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Description:
*
* This class is adapted from org.eclipse.compare.internal.DocLineComparator
* and is used to compare lines in two or three-way compares
*
* Contributors:
* Sebastien Dubois - Created for Mylyn Review R4E project
*
******************************************************************************/
package org.eclipse.mylyn.reviews.r4e.ui.internal.utils;
import org.eclipse.compare.contentmergeviewer.ITokenComparator;
import org.eclipse.compare.rangedifferencer.IRangeComparator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
/**
* Implements the <code>IRangeComparator</code> interface for lines in a document. A <code>DocLineComparator</code> is
* used as the input for the <code>RangeDifferencer</code> engine to perform a line oriented compare on documents.
* <p>
* A <code>DocLineComparator</code> doesn't know anything about line separators because its notion of lines is solely
* defined in the underlying <code>IDocument</code>.
* @author Sebastien Dubois
* @version $Revision: 1.0 $
*/
public class DocLineComparator implements ITokenComparator {
/**
* Field fDocument.
*/
private final IDocument fDocument;
/**
* Field fLineOffset.
*/
private int fLineOffset;
/**
* Field fLineCount.
*/
private int fLineCount;
/**
* Field fLength.
*/
private int fLength;
/**
* Field fIgnoreWhiteSpace.
*/
private final boolean fIgnoreWhiteSpace;
/**
* Creates a <code>DocLineComparator</code> for the given document range. ignoreWhiteSpace controls whether
* comparing lines (in method <code>rangesEqual<code>) should ignore whitespace.
*
* @param document
* the document from which the lines are taken
* @param region
* if non-<code>null</code> only lines within this range are taken
* @param ignoreWhiteSpace
* if <code>true</code> white space is ignored when comparing lines
*/
public DocLineComparator(IDocument document, IRegion region, boolean ignoreWhiteSpace) {
fDocument = document;
fIgnoreWhiteSpace = ignoreWhiteSpace;
fLineOffset = 0;
if (null != region) {
fLength = region.getLength();
final int start = region.getOffset();
try {
fLineOffset = fDocument.getLineOfOffset(start);
} catch (BadLocationException ex) {
// silently ignored
}
if (0 == fLength) {
// optimization, empty documents have one line
fLineCount = 1;
} else {
int endLine = fDocument.getNumberOfLines();
try {
endLine = fDocument.getLineOfOffset(start + fLength);
} catch (BadLocationException ex) {
// silently ignored
}
fLineCount = endLine - fLineOffset + 1;
}
} else {
fLength = document.getLength();
fLineCount = fDocument.getNumberOfLines();
}
}
/**
* Returns the number of lines in the document.
*
* @return number of lines * @see org.eclipse.compare.rangedifferencer.IRangeComparator#getRangeCount()
*/
public int getRangeCount() {
return fLineCount;
}
/* (non Javadoc)
* see ITokenComparator.getTokenStart
*/
/**
* Method getTokenStart.
* @param line int
* @return int
* @see org.eclipse.compare.contentmergeviewer.ITokenComparator#getTokenStart(int)
*/
public int getTokenStart(int line) {
try {
final IRegion r = fDocument.getLineInformation(fLineOffset + line);
return r.getOffset();
} catch (BadLocationException ex) {
return fDocument.getLength();
}
}
/* (non Javadoc)
* Returns the length of the given line.
* see ITokenComparator.getTokenLength
*/
/**
* Method getTokenLength.
* @param line int
* @return int
* @see org.eclipse.compare.contentmergeviewer.ITokenComparator#getTokenLength(int)
*/
public int getTokenLength(int line) {
return getTokenStart(line + 1) - getTokenStart(line);
}
/**
* Returns <code>true</code> if a line given by the first index matches a line specified by the other
* <code>IRangeComparator</code> and index.
*
* @param thisIndex
* the number of the line within this range comparator
* @param otherComparator
* the range comparator to compare this with
* @param otherIndex
* the number of the line within the other comparator
* @return <code>true</code> if the lines are equal * @see org.eclipse.compare.rangedifferencer.IRangeComparator#rangesEqual(int, IRangeComparator, int)
*/
public boolean rangesEqual(int thisIndex, IRangeComparator otherComparator, int otherIndex) {
if (null != otherComparator && otherComparator.getClass().equals(getClass())) {
final DocLineComparator other = (DocLineComparator) otherComparator;
if (fIgnoreWhiteSpace) {
final String s1 = extract(thisIndex);
final String s2 = other.extract(otherIndex);
//return s1.trim().equals(s2.trim());
return compare(s1, s2);
}
final int tlen = getTokenLength(thisIndex);
final int olen = other.getTokenLength(otherIndex);
if (tlen == olen) {
final String s1 = extract(thisIndex);
final String s2 = other.extract(otherIndex);
return s1.equals(s2);
}
}
return false;
}
/**
* Aborts the comparison if the number of tokens is too large.
*
* @param length
* a number on which to base the decision whether to return <code>true</code> or <code>false</code>
* @param maxLength
* another number on which to base the decision whether to return <code>true</code> or <code>false</code>
* @param other
* the other <code>IRangeComparator</code> to compare with
* @return <code>true</code> to avoid a too lengthy range comparison * @see org.eclipse.compare.rangedifferencer.IRangeComparator#skipRangeComparison(int, int, IRangeComparator)
*/
public boolean skipRangeComparison(int length, int maxLength, IRangeComparator other) {
return false;
}
//---- private methods
/**
* Extract a single line from the underlying document without the line separator.
*
* @param line
* the number of the line to extract
* @return the contents of the line as a String */
private String extract(int line) {
if (line < fLineCount) {
try {
final IRegion r = fDocument.getLineInformation(fLineOffset + line);
return fDocument.get(r.getOffset(), r.getLength());
} catch (BadLocationException e) {
// silently ignored
}
}
return ""; //$NON-NLS-1$
}
/**
* Method compare.
* @param s1 String
* @param s2 String
* @return boolean
*/
private boolean compare(String s1, String s2) {
final int l1 = s1.length();
final int l2 = s2.length();
int c1 = 0, c2 = 0;
int i1 = 0, i2 = 0;
while (c1 != -1) {
c1 = -1;
while (i1 < l1) {
char c = s1.charAt(i1++);
if (!Character.isWhitespace(c)) {
c1 = c;
break;
}
}
c2 = -1;
while (i2 < l2) {
char c = s2.charAt(i2++);
if (!Character.isWhitespace(c)) {
c2 = c;
break;
}
}
if (c1 != c2) {
return false;
}
}
return true;
}
}