/******************************************************************************* | |
* 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; | |
} | |
} |