All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cdc.applic.expressions.parsing.SItemsParsing Maven / Gradle / Ivy

There is a newer version: 0.13.3
Show newest version
package cdc.applic.expressions.parsing;

import java.util.ArrayList;
import java.util.List;

import cdc.applic.expressions.SyntacticException;
import cdc.applic.expressions.content.BooleanSet;
import cdc.applic.expressions.content.BooleanValue;
import cdc.applic.expressions.content.IntegerRange;
import cdc.applic.expressions.content.IntegerSet;
import cdc.applic.expressions.content.IntegerValue;
import cdc.applic.expressions.content.Range;
import cdc.applic.expressions.content.RealRange;
import cdc.applic.expressions.content.RealSet;
import cdc.applic.expressions.content.RealValue;
import cdc.applic.expressions.content.SItem;
import cdc.applic.expressions.content.StringSet;
import cdc.applic.expressions.content.StringValue;
import cdc.applic.expressions.content.UncheckedSet;
import cdc.applic.expressions.content.Value;
import cdc.util.lang.Checks;

/**
 * Utilities to convert the string representation of a set content to {@link SItem SItems}.
 *
 * @author Damien Carbonne
 */
public final class SItemsParsing {
    private SItemsParsing() {
    }

    /**
     * Creates a {@link SyntacticException} corresponding to an unexpected token.
     *
     * @param content The set content.
     * @param token The {@link Token}.
     * @return A new {@link SyntacticException}.
     */
    private static SyntacticException unexpectedToken(String content,
                                                      Token token) {
        return new SyntacticException(SyntacticException.Detail.UNEXPECTED_TOKEN,
                                      "[" + content + "] unexpected token: " + token);
    }

    /**
     * Creates a {@link SyntacticException} corresponding to an unexpected end.
     *
     * @param content The set content.
     * @return A new {@link SyntacticException}.
     */
    private static SyntacticException unexpectedEnd(String content) {
        return new SyntacticException(SyntacticException.Detail.UNEXPECTED_END,
                                      "[" + content + "] unexpected end");
    }

    private enum Status {
        /** At parsing start */
        START,
        /** After Range */
        RANGE,
        /** After (first) value */
        VALUE,
        /** After values Separator */
        VALUES_SEP,
        /** After Range symbol */
        TO
    }

    /**
     * Creates a {@link Value} from a {@link Token}.
     *
     * @param token The {@link Token}.
     * @param expression The expression that contains the token.
     * @return A {@link Value} corresponding to {@code token}.
     * @throws SyntacticException When {@code token} can not be converted to a {@link Value}.
     */
    public static Value createValue(Token token,
                                    String expression) {
        try {
            return createValue(token);
        } catch (final IllegalArgumentException e) {
            throw new SyntacticException(SyntacticException.Detail.UNEXPECTED_TOKEN,
                                         e.getMessage(),
                                         expression);
        }
    }

    /**
     * Creates a {@link Value} from a {@link Token}.
     *
     * @param token The {@link Token}.
     * @return A {@link Value} corresponding to {@code token}.
     * @throws IllegalArgumentException When {@code token} can not be converted to a {@link Value}.
     */
    private static Value createValue(Token token) {
        switch (token.getType()) {
        case ESCAPED_TEXT:
        case TEXT:
            return StringValue.of(token.getUnescapedText(), false);
        case INTEGER:
            return IntegerValue.of(token.getUnescapedText());
        case REAL:
            return RealValue.of(token.getUnescapedText());
        case TRUE:
            return BooleanValue.TRUE;
        case FALSE:
            return BooleanValue.FALSE;
        default:
            throw new IllegalArgumentException("Can not create a value with " + token);
        }
    }

    /**
     * Creates a {@link Range} from 2 {@link Value Values}.
     *
     * @param low The lower bound {@link Value}.
     * @param high The higher bound {@link Value}.
     * @param expression The expression that contains the 2 values.
     * @return A new {@link Range} from {@code low} and {@code high}.
     * @throws SyntacticException When {@code low} and {@code high} can not be converted to a {@link Range}.
     */
    public static Range createRange(Value low,
                                    Value high,
                                    String expression) {
        try {
            return createRange(low, high);
        } catch (final IllegalArgumentException e) {
            throw new SyntacticException(SyntacticException.Detail.INVALID_RANGE,
                                         e.getMessage(),
                                         expression);
        }
    }

    /**
     * Creates a {@link Range} from 2 {@link Value Values}.
     *
     * @param low The lower bound {@link Value}.
     * @param high The higher bound {@link Value}.
     * @return A new {@link Range} from {@code low} and {@code high}.
     * @throws IllegalArgumentException When {@code low} and {@code high} can not be converted to a {@link Range}.
     */
    public static Range createRange(Value low,
                                    Value high) {
        if (low.getClass().equals(high.getClass())) {
            if (IntegerValue.class.equals(low.getClass())) {
                return IntegerRange.of(((IntegerValue) low).getNumber(),
                                       ((IntegerValue) high).getNumber());
            } else if (RealValue.class.equals(low.getClass())) {
                return RealRange.of(((RealValue) low).getNumber(),
                                    ((RealValue) high).getNumber());
            } else {
                throw new IllegalArgumentException("Can not create a range with " + low + " and " + high);
            }
        } else {
            throw new IllegalArgumentException("Can not create a range with " + low + " and " + high);
        }
    }

    /**
     * Creates a {@link List} of {@link BooleanValue BooleanValues} from a set content.
     *
     * @param content The content.
     * @return A {@link List} of {@link BooleanValue BooleanValues} from {@code content}.
     * @throws SyntacticException When {@code content} can not be parsed as the content of a {@link BooleanSet}.
     */
    public static List toBooleanValues(String content) {
        Checks.isNotNull(content, "content");

        final List result = new ArrayList<>();
        final Tokenizer tokenizer = new Tokenizer();
        tokenizer.init(content);
        Status status = Status.START;
        while (tokenizer.hasMoreTokens()) {
            final Token token = tokenizer.nextToken(false);
            final BooleanValue value;
            switch (token.getType()) {
            case FALSE:
                if (status == Status.VALUE) {
                    throw unexpectedToken(content, token);
                } else {
                    value = BooleanValue.FALSE;
                    status = Status.VALUE;
                }
                break;

            case TRUE:
                if (status == Status.VALUE) {
                    throw unexpectedToken(content, token);
                } else {
                    value = BooleanValue.TRUE;
                    status = Status.VALUE;
                }
                break;

            case ITEMS_SEP:
                if (status == Status.VALUES_SEP || status == Status.START) {
                    throw unexpectedToken(content, token);
                } else {
                    value = null;
                    status = Status.VALUES_SEP;
                }
                break;
            default:
                throw unexpectedToken(content, token);
            }

            if (value != null && !result.contains(value)) {
                result.add(value);
            }
        }
        if (status == Status.VALUES_SEP) {
            throw unexpectedEnd(content);
        }
        return result;
    }

    /**
     * Creates a {@link List} of {@link StringValue StringValues} from a set content.
     *
     * @param content The content.
     * @return A {@link List} of {@link StringValue StringValues} from {@code content}.
     * @throws SyntacticException When {@code content} can not be parsed as the content of a {@link StringSet}.
     */
    public static List toStringValues(String content) {
        final List result = new ArrayList<>();

        Status status = Status.START;
        final Tokenizer tokenizer = new Tokenizer();
        tokenizer.init(content);
        while (tokenizer.hasMoreTokens()) {
            final Token token = tokenizer.nextToken(false);
            final StringValue value;
            switch (token.getType()) {
            case ESCAPED_TEXT:
            case TEXT:
                if (status == Status.VALUE) {
                    throw unexpectedToken(content, token);
                } else {
                    value = StringValue.of(token.getUnescapedText(), false);
                    status = Status.VALUE;
                }
                break;

            case ITEMS_SEP:
                if (status == Status.VALUES_SEP || status == Status.START) {
                    throw unexpectedToken(content, token);
                } else {
                    value = null;
                    status = Status.VALUES_SEP;
                }
                break;

            default:
                throw unexpectedToken(content, token);
            }
            if (value != null && !result.contains(value)) {
                result.add(value);
            }
        }

        if (status == Status.VALUES_SEP) {
            throw unexpectedEnd(content);
        }

        return result;
    }

    /**
     * Creates a {@link List} of {@link IntegerRange IntegerRanges} from a set content.
     * 

* Note: single values are interpreted as ranges. * * @param content The content. * @return A {@link List} of {@link IntegerRange IntegerRanges} from {@code content}. * @throws SyntacticException When {@code content} can not be parsed as the content of an {@link IntegerSet}. */ public static List toIntegerRanges(String content) { final List result = new ArrayList<>(); Status status = Status.START; int low = 0; final Tokenizer tokenizer = new Tokenizer(); tokenizer.init(content); while (tokenizer.hasMoreTokens()) { final Token token = tokenizer.nextToken(false); switch (token.getType()) { case INTEGER: if (status == Status.VALUE || status == Status.RANGE) { throw unexpectedToken(content, token); } else { final String text = token.getUnescapedText(); final IntegerValue value = IntegerValue.of(text); final int number = value.getNumber(); if (status == Status.START || status == Status.VALUES_SEP) { low = number; status = Status.VALUE; } else { // status == Status.TO result.add(IntegerRange.of(low, number)); status = Status.RANGE; } } break; case ITEMS_SEP: if (status == Status.VALUE) { result.add(IntegerRange.of(low)); status = Status.VALUES_SEP; } else if (status == Status.RANGE) { status = Status.VALUES_SEP; } else { throw unexpectedToken(content, token); } break; case TO: if (status == Status.VALUE) { status = Status.TO; } else { throw unexpectedToken(content, token); } break; default: throw unexpectedToken(content, token); } } if (status == Status.VALUE) { result.add(IntegerRange.of(low)); } else if (status == Status.RANGE) { // Ignore } else if (status != Status.START) { throw unexpectedEnd(content); } return result; } /** * Creates a {@link List} of {@link RealRange RealRanges} from a set content. *

* Note: single values are interpreted as ranges. * * @param content The content. * @return A {@link List} of {@link RealRange RealRanges} from {@code content}. * @throws SyntacticException When {@code content} can not be parsed as the content of a {@link RealSet}. */ public static List toRealRanges(String content) { final List result = new ArrayList<>(); Status status = Status.START; double low = 0.0; final Tokenizer tokenizer = new Tokenizer(); tokenizer.init(content); while (tokenizer.hasMoreTokens()) { final Token token = tokenizer.nextToken(false); switch (token.getType()) { case REAL: if (status == Status.VALUE || status == Status.RANGE) { throw unexpectedToken(content, token); } else { final String text = token.getUnescapedText(); final RealValue value = RealValue.of(text); final double number = value.getNumber(); if (status == Status.START || status == Status.VALUES_SEP) { low = number; status = Status.VALUE; } else { // status == Status.TO result.add(RealRange.of(low, number)); status = Status.RANGE; } } break; case ITEMS_SEP: if (status == Status.VALUE) { result.add(RealRange.of(low)); status = Status.VALUES_SEP; } else if (status == Status.RANGE) { status = Status.VALUES_SEP; } else { throw unexpectedToken(content, token); } break; case TO: if (status == Status.VALUE) { status = Status.TO; } else { throw unexpectedToken(content, token); } break; default: throw unexpectedToken(content, token); } } if (status == Status.VALUE) { result.add(RealRange.of(low)); } else if (status == Status.RANGE) { // Ignore } else if (status != Status.START) { throw unexpectedEnd(content); } return result; } /** * Creates a {@link List} of {@link SItem SItems} from a set content. * * @param content The content. * @return A {@link List} of {@link SItem SItems} from {@code content}. * @throws SyntacticException When {@code content} can not be parsed as the content of an {@link UncheckedSet}. */ public static List toSItems(String content) { final List result = new ArrayList<>(); Status status = Status.START; Value previousValue = null; final Tokenizer tokenizer = new Tokenizer(); tokenizer.init(content); while (tokenizer.hasMoreTokens()) { final Token token = tokenizer.nextToken(false); switch (token.getType()) { case TEXT: case ESCAPED_TEXT: case TRUE: case FALSE: case INTEGER: case REAL: if (status == Status.VALUE) { throw unexpectedToken(content, token); } else if (status == Status.START || status == Status.RANGE || status == Status.VALUES_SEP) { previousValue = createValue(token, content); status = Status.VALUE; } else { // status == Status.TO final Value value = createValue(token, content); final Range range = createRange(previousValue, value, content); result.add(range); status = Status.RANGE; } break; case ITEMS_SEP: if (status == Status.VALUE) { result.add(previousValue); status = Status.VALUES_SEP; } else if (status == Status.RANGE) { // Ignore } else { throw unexpectedToken(content, token); } break; case TO: if (status == Status.VALUE) { status = Status.TO; } else { throw unexpectedToken(content, token); } break; default: throw unexpectedToken(content, token); } } if (status == Status.VALUE) { result.add(previousValue); } else if (status == Status.RANGE) { // Ignore } else if (status != Status.START) { throw unexpectedEnd(content); } return result; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy