/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation 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
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.rubypeople.rdt.internal.ui.compare;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.compare.contentmergeviewer.ITokenComparator;
import org.eclipse.compare.rangedifferencer.IRangeComparator;
import org.eclipse.core.runtime.Assert;
import org.rubypeople.rdt.core.ToolFactory;
import org.rubypeople.rdt.core.compiler.IScanner;
import org.rubypeople.rdt.core.compiler.InvalidInputException;
/**
* A comparator for Ruby tokens.
*/
public class RubyTokenComparator implements ITokenComparator {
private String fText;
private boolean fShouldEscape= true;
private List<Integer> fStarts;
private List<Integer> fLengths;
/**
* Creates a TokenComparator for the given string.
*/
public RubyTokenComparator(String text, boolean shouldEscape) {
Assert.isNotNull(text);
fText= text;
fShouldEscape= shouldEscape;
int length= fText.length();
fStarts= new ArrayList<Integer>(length + 1); // try to never grow list
fLengths= new ArrayList<Integer>(length + 1);
IScanner scanner= ToolFactory.createScanner(true, true, false, false); // returns comments & whitespace
scanner.setSource(fText.toCharArray());
try {
int endPos= 0;
while (scanner.getNextToken() != IScanner.TokenNameEOF) {
int start= scanner.getCurrentTokenStartPosition();
int end= scanner.getCurrentTokenEndPosition()+1;
fStarts.add(start);
fLengths.add(end - start);
endPos= end;
// What if the scanner goes nuts and never returns EOF? ugly escape valve here
if (fStarts.size() > (length * 2))
break;
}
// workaround for #13907
if (endPos < length) {
fStarts.add(endPos);
fLengths.add(length-endPos);
}
} catch (InvalidInputException ex) {
// NeedWork
}
}
/**
* Returns the number of token in the string.
*
* @return number of token in the string
*/
public int getRangeCount() {
return fStarts.size();
}
/* (non Rubydoc)
* see ITokenComparator.getTokenStart
*/
public int getTokenStart(int index) {
if (index >= 0 && index < getRangeCount())
return fStarts.get(index);
if (getRangeCount() > 0)
return fStarts.get(getRangeCount()-1) + fLengths.get(getRangeCount()-1);
return 0;
}
/* (non Rubydoc)
* see ITokenComparator.getTokenLength
*/
public int getTokenLength(int index) {
if (index < getRangeCount())
return fStarts.get(index);
return 0;
}
/**
* Returns <code>true</code> if a token given by the first index
* matches a token specified by the other <code>IRangeComparator</code> and index.
*
* @param thisIndex the number of the token within this range comparator
* @param other the range comparator to compare this with
* @param otherIndex the number of the token within the other comparator
* @return <code>true</code> if the token are equal
*/
public boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex) {
if (other != null && getClass() == other.getClass()) {
RubyTokenComparator tc= (RubyTokenComparator) other; // safe cast
int thisLen= getTokenLength(thisIndex);
int otherLen= tc.getTokenLength(otherIndex);
if (thisLen == otherLen)
return fText.regionMatches(false, getTokenStart(thisIndex), tc.fText, tc.getTokenStart(otherIndex), thisLen);
}
return false;
}
/**
* Aborts the comparison if the number of tokens is too large.
*
* @return <code>true</code> to abort a token comparison
*/
public boolean skipRangeComparison(int length, int max, IRangeComparator other) {
if (!fShouldEscape)
return false;
if (getRangeCount() < 50 || other.getRangeCount() < 50)
return false;
if (max < 100)
return false;
if (length < 100)
return false;
if (max > 800)
return true;
if (length < max / 4)
return false;
return true;
}
}