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