blob: b9c9d18f1c2a4c9b1c8937df7513981565a8a734 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2016, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.core.data;
import org.eclipse.statet.jcommons.text.core.input.StringParserInput;
import org.eclipse.statet.r.core.rlang.RTerminal;
import org.eclipse.statet.r.core.rsource.IRSourceConstants;
import org.eclipse.statet.r.core.rsource.RLexer;
import org.eclipse.statet.rj.data.RCharacterStore;
import org.eclipse.statet.rj.data.RComplexStore;
import org.eclipse.statet.rj.data.RFactorStore;
import org.eclipse.statet.rj.data.RIntegerStore;
import org.eclipse.statet.rj.data.RLogicalStore;
import org.eclipse.statet.rj.data.RNumericStore;
import org.eclipse.statet.rj.data.RRawStore;
import org.eclipse.statet.rj.data.RStore;
import org.eclipse.statet.rj.data.impl.DefaultRObjectFactory;
public class RValueValidator {
private static final byte INVALID= 0;
private static final byte UNKNOWN= 1;
private static final byte VALID= 2;
private int addSign(final int sign, final int value) {
return (sign == -1) ? -value : value;
}
private double addSign(final int sign, final double value) {
return (sign == -1) ? -value : value;
}
private final RLexer lexer= new RLexer(RLexer.ENABLE_NUM_VALUE);
private final RLogicalStore logiValue= DefaultRObjectFactory.INSTANCE.createLogiData(1);
private final RIntegerStore intValue= DefaultRObjectFactory.INSTANCE.createIntData(1);
private final RNumericStore numValue= DefaultRObjectFactory.INSTANCE.createNumData(1);
private final RComplexStore complexValue= DefaultRObjectFactory.INSTANCE.createCplxData(1);
private final RCharacterStore charValue= DefaultRObjectFactory.INSTANCE.createCharData(1);
private final RRawStore rawValue= DefaultRObjectFactory.INSTANCE.createRawData(1);
private RValueFormatter formatter;
public RValueValidator() {
}
public boolean isValid(final RStore<?> store, String expression) {
switch (store.getStoreType()) {
case RStore.LOGICAL:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
return true;
case RStore.NUMERIC:
return (real2logi());
default:
return false;
}
case RStore.INTEGER:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
case RStore.INTEGER:
return true;
case RStore.NUMERIC:
return (real2int());
default:
return false;
}
case RStore.NUMERIC:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
case RStore.INTEGER:
case RStore.NUMERIC:
return true;
default:
return false;
}
case RStore.COMPLEX:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
case RStore.INTEGER:
case RStore.NUMERIC:
case RStore.COMPLEX:
return true;
default:
return false;
}
case RStore.CHARACTER:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
case RStore.INTEGER:
case RStore.NUMERIC:
case RStore.COMPLEX:
case RStore.CHARACTER:
return true;
default:
return false;
}
case RStore.RAW:
expression= expression.trim();
switch (parseSimpleHexInt(expression)) {
case VALID:
return (int2raw());
case UNKNOWN:
switch (parseValueType(expression)) {
case RStore.LOGICAL:
return (!this.logiValue.isNA(0));
case RStore.NUMERIC:
return (expression.startsWith("0x") && real2int() && int2raw());
case RStore.INTEGER:
return (int2raw());
default:
return false;
}
default:
return false;
}
case RStore.FACTOR:
return parseFactorLevel(expression, ((RFactorStore) store).getLevels());
default:
return false;
}
}
public RStore<?> toRData(final RStore<?> store, String expression) {
switch (store.getStoreType()) {
case RStore.LOGICAL:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
return this.logiValue;
case RStore.NUMERIC:
if (real2logi()) {
return this.logiValue;
}
return null;
default:
return null;
}
case RStore.INTEGER:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
return this.logiValue;
case RStore.INTEGER:
return this.intValue;
case RStore.NUMERIC:
if (real2int()) {
return this.intValue;
}
return null;
default:
return null;
}
case RStore.NUMERIC:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
return this.logiValue;
case RStore.INTEGER:
return this.intValue;
case RStore.NUMERIC:
return this.numValue;
default:
return null;
}
case RStore.COMPLEX:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
return this.logiValue;
case RStore.INTEGER:
return this.intValue;
case RStore.NUMERIC:
return this.numValue;
case RStore.COMPLEX:
return this.complexValue;
default:
return null;
}
case RStore.CHARACTER:
expression= expression.trim();
switch (parseValueType(expression)) {
case RStore.LOGICAL:
return this.logiValue;
case RStore.INTEGER:
return this.intValue;
case RStore.NUMERIC:
return this.numValue;
case RStore.COMPLEX:
return this.complexValue;
case RStore.CHARACTER:
return this.charValue;
default:
return null;
}
case RStore.RAW:
expression= expression.trim();
switch (parseSimpleHexInt(expression)) {
case VALID:
if (int2raw()) {
return this.rawValue;
}
return null;
case UNKNOWN:
switch (parseValueType(expression)) {
case RStore.LOGICAL:
if (!this.logiValue.isNA(0)) {
this.rawValue.setRaw(0, this.logiValue.getRaw(0));
return this.rawValue;
}
return null;
case RStore.NUMERIC:
if (expression.startsWith("0x") && real2int() && int2raw()) {
return this.rawValue;
}
return null;
case RStore.INTEGER:
if (int2raw()) {
return this.rawValue;
}
return null;
default:
return null;
}
default:
return null;
}
case RStore.FACTOR:
if (parseFactorLevel(expression, ((RFactorStore) store).getLevels())) {
return this.charValue;
}
return null;
default:
return null;
}
}
private byte parseValueType(final String expression) {
this.lexer.reset(new StringParserInput(expression).init());
RTerminal nextToken= this.lexer.next();
int sign;
switch (nextToken) {
case PLUS:
sign= +1;
nextToken= this.lexer.next();
break;
case MINUS:
sign= -1;
nextToken= this.lexer.next();
break;
default:
sign= 0;
break;
}
if ((this.lexer.getFlags() & IRSourceConstants.STATUSFLAG_REAL_ERROR) != 0) {
return INVALID;
}
switch (nextToken) {
case TRUE:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.logiValue.setLogi(0, true);
return RStore.LOGICAL;
}
break;
default:
break;
}
return INVALID;
case FALSE:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.logiValue.setLogi(0, false);
return RStore.LOGICAL;
}
break;
default:
break;
}
return INVALID;
case NA:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.logiValue.setNA(0);
return RStore.LOGICAL;
}
break;
default:
break;
}
return INVALID;
case NUM_INT: {
final int value= (int) this.lexer.getNumValue();
switch (this.lexer.next()) {
case EOF:
this.intValue.setInt(0, addSign(sign, value));
return RStore.INTEGER;
default:
break;
}
return INVALID; }
case NA_INT:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.intValue.setNA(0);
return RStore.INTEGER;
}
break;
default:
break;
}
return INVALID;
case NUM_NUM:
case INF: {
double value= this.lexer.getNumValue();
switch (this.lexer.next()) {
case EOF:
this.numValue.setNum(0, addSign(sign, value));
return RStore.NUMERIC;
case PLUS:
case MINUS:
this.complexValue.setNum(0, addSign(sign, value));
sign= (this.lexer.getType() == RTerminal.PLUS) ? 1 : -1;
nextToken= this.lexer.next();
if ((this.lexer.getFlags() & IRSourceConstants.STATUSFLAG_REAL_ERROR) != 0) {
return INVALID;
}
switch (nextToken) {
case NUM_CPLX:
value= this.lexer.getNumValue();
switch (this.lexer.next()) {
case EOF:
this.complexValue.setCplx(0, this.complexValue.getCplxRe(0),
addSign(sign, value) );
return RStore.COMPLEX;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
return INVALID; }
case NAN:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.numValue.setNum(0, Double.NaN);
return RStore.NUMERIC;
}
break;
default:
break;
}
return INVALID;
case NA_REAL:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.numValue.setNA(0);
return RStore.NUMERIC;
}
break;
default:
break;
}
return INVALID;
case NUM_CPLX: {
final double value= this.lexer.getNumValue();
switch (this.lexer.next()) {
case EOF:
this.complexValue.setCplx(0, 0, addSign(sign, value));
return RStore.COMPLEX;
default:
break;
}
return INVALID; }
case NA_CPLX:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.complexValue.setNA(0);
return RStore.COMPLEX;
}
break;
default:
break;
}
return INVALID;
case STRING_D:
case STRING_S: {
final String value= this.lexer.getText();
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.charValue.setChar(0, value);
return RStore.CHARACTER;
}
break;
default:
break;
}
return INVALID; }
case NA_CHAR:
switch (this.lexer.next()) {
case EOF:
if (sign == 0) {
this.charValue.setNA(0);
return RStore.CHARACTER;
}
break;
default:
break;
}
return INVALID;
default:
return INVALID;
}
}
private boolean parseFactorLevel(String expression, final RCharacterStore levels) {
if (expression.indexOf('\\') >= 0) {
if (this.formatter == null) {
this.formatter= new RValueFormatter();
}
expression= this.formatter.quoteName(expression);
this.lexer.reset(new StringParserInput(expression).init());
if (this.lexer.next() != RTerminal.STRING_D
|| (this.lexer.getFlags() & IRSourceConstants.STATUSFLAG_REAL_ERROR) != 0) {
return false;
}
expression= this.lexer.getText();
switch (this.lexer.next()) {
case EOF:
break;
default:
return false;
}
}
if (levels.contains(expression)
|| (expression != (expression= expression.trim()) && levels.contains(expression)) ) {
this.charValue.setChar(0, expression);
return true;
}
if (expression.equals("NA")
|| (expression.equals("<NA>") && levels.containsNA()) ) {
this.charValue.setNA(0);
return true;
}
return false;
}
private byte parseSimpleHexInt(final String expression) {
final int l= expression.length();
if (l == 0) {
return INVALID;
}
for (int i= 0; i < l; i++) {
switch (expression.charAt(i)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
continue;
default:
return UNKNOWN;
}
}
try {
this.intValue.setInt(0, Integer.parseInt(expression, 16));
return VALID;
}
catch (final NumberFormatException e) {
return INVALID;
}
}
private boolean real2logi() {
if (!this.numValue.isNA(0)) {
final double d= this.numValue.getNum(0);
final int i= (int) d;
if (d == i && i >= 0 && i <= 1) {
this.logiValue.setInt(0, i);
return true;
}
}
return false;
}
private boolean real2int() {
if (!this.numValue.isNA(0)) {
final double d= this.numValue.getNum(0);
final int i= (int) d;
if (d == i) {
this.intValue.setInt(0, i);
return true;
}
}
return false;
}
private boolean int2raw() {
final int i;
if (!this.intValue.isNA(0)
&& (i= this.intValue.getInt(0)) >= 0 && i <= 255) {
this.rawValue.setRaw(0, (byte) i);
return true;
}
return false;
}
}