blob: 11ef5b27c1ebf859adce86399bc5d3ed894dd35d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 xored software, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
* NumberFour AG - made reusable by introducing constructor parameters (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.ui.text.rules;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.Token;
/**
* An implementation of <code>IRule</code> detecting a numerical value.
* <p>
* Supported number formats are:
* <ul>
* <li>integer
* <li>floating point (with dot and exponent)
* <li>hexadecimal (with 0x prefix)
* </ul>
* </p>
*/
public class FloatNumberRule implements IRule {
private final IToken fToken;
private final String fDigitSeparators;
private final String fDecimalSuffixes;
private final String fFloatSuffixes;
public FloatNumberRule(IToken token) {
this(token, null, null, null);
}
/**
* @param token
* @param digitSeparators
* additional digits separator, e.g. "_" for
* java7 or <code>null</code> if not applicable
* @param integerSuffixes
* integer number suffixes, e.g. "Ll" for java or
* <code>null</code> if not applicable
* @param floatSuffixes
* floating point number suffixes, e.g. "DdFf"
* for java or <code>null</code> if not
* applicable
*/
public FloatNumberRule(IToken token, String digitSeparators,
String integerSuffixes, String floatSuffixes) {
Assert.isNotNull(token);
fToken = token;
fDigitSeparators = digitSeparators;
fDecimalSuffixes = integerSuffixes;
fFloatSuffixes = floatSuffixes;
}
private enum Mode {
DECIMAL, HEX, FLOAT
}
@Override
public IToken evaluate(ICharacterScanner scanner) {
int c = scanner.read();
if (Character.isDigit((char) c) || c == '.') {
int readCount = 1;
int digitCount;
Mode mode;
if (c == '.') {
mode = Mode.FLOAT;
digitCount = 0;
} else {
mode = Mode.DECIMAL;
digitCount = 1;
}
for (;;) {
final char lastDigit = (char) c;
c = scanner.read();
++readCount;
if (c == 'x' || c == 'X') {
if (mode == Mode.DECIMAL && digitCount == 1
&& lastDigit == '0') {
mode = Mode.HEX;
digitCount = 0;
} else {
unread(scanner, readCount);
return Token.UNDEFINED; // not "0x" prefix
}
} else if (Character.isDigit((char) c) || isSeparator(c)
|| (mode == Mode.HEX && (c >= 'A' && c <= 'F'
|| c >= 'a' && c <= 'f'))) {
digitCount++;
} else if (c == '.' && mode == Mode.DECIMAL) {
mode = Mode.FLOAT;
} else {
break;
}
}
if (digitCount == 0) { // "0x" or "." only
unread(scanner, readCount);
return Token.UNDEFINED;
}
if ((c == 'e' || c == 'E')
&& (mode == Mode.DECIMAL || mode == Mode.FLOAT)) {
mode = Mode.FLOAT;
c = scanner.read();
++readCount;
if (c == '+' || c == '-') {
c = scanner.read();
++readCount;
}
if (Character.isDigit((char) c)) {
do {
++readCount;
c = scanner.read();
} while (Character.isDigit((char) c) || isSeparator(c));
} else {
unread(scanner, readCount);
return Token.UNDEFINED;
}
}
if (!(((mode == Mode.DECIMAL || mode == Mode.FLOAT)
&& isFloatSuffix(c))
|| (mode == Mode.DECIMAL && isDecimalSuffix(c)))) {
scanner.unread();
}
return fToken;
}
scanner.unread();
return Token.UNDEFINED;
}
private final void unread(ICharacterScanner scanner, int count) {
while (--count >= 0) {
scanner.unread();
}
}
private final boolean isSeparator(int c) {
return fDigitSeparators != null && fDigitSeparators.indexOf(c) >= 0;
}
private final boolean isDecimalSuffix(int c) {
return fDecimalSuffixes != null && fDecimalSuffixes.indexOf(c) >= 0;
}
private final boolean isFloatSuffix(int c) {
return fFloatSuffixes != null && fFloatSuffixes.indexOf(c) >= 0;
}
}