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

tech.ytsaurus.core.cypress.RichYPath Maven / Gradle / Ivy

package tech.ytsaurus.core.cypress;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Nullable;

import tech.ytsaurus.core.GUID;
import tech.ytsaurus.lang.NonNullApi;
import tech.ytsaurus.lang.NonNullFields;
import tech.ytsaurus.yson.YsonTokenType;
import tech.ytsaurus.ysontree.YTree;
import tech.ytsaurus.ysontree.YTreeBuilder;
import tech.ytsaurus.ysontree.YTreeMapNode;
import tech.ytsaurus.ysontree.YTreeNode;
import tech.ytsaurus.ysontree.YTreeTextSerializer;


@NonNullFields
public class RichYPath implements YPath {

    private String rootDesignator;
    private List relativePath;

    private @Nullable Boolean append;
    private @Nullable Boolean primary;
    private @Nullable Boolean foreign;
    private List ranges;
    private List columns;
    private Map renameColumns;
    private List sortedBy;
    private @Nullable Long timestamp;
    private @Nullable YTreeNode schema;
    private @Nullable String format;
    private @Nullable Boolean bypassArtifactCache;
    private @Nullable Boolean executable;
    private @Nullable Boolean create;

    private Map additionalAttributes;

    RichYPath(String rootDesignator, List relativePath) {
        this.rootDesignator = rootDesignator;
        this.relativePath = relativePath;
        this.append = null;
        this.primary = null;
        this.foreign = null;
        this.ranges = List.of();
        this.columns = List.of();
        this.renameColumns = Map.of();
        this.sortedBy = List.of();
        this.timestamp = null;
        this.schema = null;
        this.format = null;
        this.bypassArtifactCache = null;
        this.executable = null;
        this.create = null;

        this.additionalAttributes = Map.of();
    }

    private RichYPath(RichYPath other, List relativePath) {
        this.rootDesignator = other.rootDesignator;
        this.relativePath = relativePath;

        this.append = other.append;
        this.primary = other.primary;
        this.ranges = other.ranges;
        this.columns = other.columns;
        this.renameColumns = other.renameColumns;
        this.foreign = other.foreign;
        this.sortedBy = other.sortedBy;
        this.timestamp = other.timestamp;
        this.schema = other.schema;
        this.format = other.format;
        this.bypassArtifactCache = other.bypassArtifactCache;
        this.executable = other.executable;
        this.create = other.create;

        this.additionalAttributes = other.additionalAttributes;
    }

    private RichYPath(RichYPath other) {
        this(other, other.relativePath);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        RichYPath richYPath = (RichYPath) o;
        return Objects.equals(rootDesignator, richYPath.rootDesignator)
                && Objects.equals(relativePath, richYPath.relativePath)
                && Objects.equals(append, richYPath.append)
                && Objects.equals(primary, richYPath.primary)
                && Objects.equals(foreign, richYPath.foreign)
                && Objects.equals(ranges, richYPath.ranges)
                && Objects.equals(columns, richYPath.columns)
                && Objects.equals(renameColumns, richYPath.renameColumns)
                && Objects.equals(sortedBy, richYPath.sortedBy)
                && Objects.equals(timestamp, richYPath.timestamp)
                && Objects.equals(schema, richYPath.schema)
                && Objects.equals(format, richYPath.format)
                && Objects.equals(bypassArtifactCache, richYPath.bypassArtifactCache)
                && Objects.equals(executable, richYPath.executable)
                && Objects.equals(create, richYPath.create)
                && Objects.equals(additionalAttributes, richYPath.additionalAttributes);
    }

    @Override
    public int hashCode() {
        return Objects.hash(
                rootDesignator,
                relativePath,
                append,
                primary,
                foreign,
                ranges,
                columns,
                renameColumns,
                sortedBy,
                timestamp,
                schema,
                format,
                bypassArtifactCache,
                executable,
                create,
                additionalAttributes
        );
    }

    /**
     * Get path without RichPath additional attributes.
     */
    @Override
    public YPath justPath() {
        return new RichYPath(rootDesignator, relativePath);
    }

    /**
     * Get last component of the path (e.g. `//foo/bar` --- `bar`).
     */
    @Override
    public String name() {
        if (isAttribute()) {
            throw new IllegalStateException();
        }
        return relativePath.get(relativePath.size() - 1);
    }

    @Override
    public boolean isRoot() {
        return relativePath.isEmpty();
    }

    @Override
    public boolean isAttribute() {
        return !relativePath.isEmpty() && relativePath.get(relativePath.size() - 1).startsWith("@");
    }

    @Override
    public boolean hasObjectRootDesignator() {
        return rootDesignator.startsWith("#");
    }

    @Override
    public YPath withObjectRoot(GUID id) {
        RichYPath copy = new RichYPath(this);
        copy.rootDesignator = "#" + id;
        copy.relativePath = List.of();
        return copy;
    }

    @Override
    public YPath parent() {
        if (relativePath.isEmpty()) {
            return this;
        }
        List newRelativePath = List.copyOf(relativePath.subList(0, relativePath.size() - 1));
        return new RichYPath(this, newRelativePath);
    }

    @Override
    public YPath child(String key) {
        List list = new ArrayList<>(relativePath);
        list.addAll(getRelativePath("/".concat(key), 0));

        List newRelativePath = List.copyOf(list);
        return new RichYPath(this, newRelativePath);
    }

    @Override
    public YPath after(int index) {
        List list = new ArrayList<>(relativePath);
        list.add("after:" + index);

        List newRelativePath = List.copyOf(list);
        return new RichYPath(this, newRelativePath);
    }

    @Override
    public YPath before(int index) {
        List list = new ArrayList<>(relativePath);
        list.add("before:" + index);

        List newRelativePath = List.copyOf(list);
        return new RichYPath(this, newRelativePath);
    }

    @Override
    public YPath begin() {
        List list = new ArrayList<>(relativePath);
        list.add("begin");

        List newRelativePath = List.copyOf(list);
        return new RichYPath(this, newRelativePath);
    }

    @Override
    public YPath end() {
        List list = new ArrayList<>(relativePath);
        list.add("end");

        List newRelativePath = List.copyOf(list);
        return new RichYPath(this, newRelativePath);
    }

    @Override
    public YPath child(int index) {
        List list = new ArrayList<>(relativePath);
        list.add(String.valueOf(index));

        RichYPath copy = new RichYPath(this);
        copy.relativePath = List.copyOf(list);
        return copy;
    }

    @Override
    public YPath attribute(String key) {
        List list = new ArrayList<>(relativePath);
        list.add("@" + key);

        RichYPath copy = new RichYPath(this);
        copy.relativePath = List.copyOf(list);
        return copy;
    }

    @Override
    public YPath all() {
        List list = new ArrayList<>(relativePath);
        list.add("*");

        RichYPath copy = new RichYPath(this);
        copy.relativePath = List.copyOf(list);
        return copy;
    }

    @Override
    public YPath allAttributes() {
        List list = new ArrayList<>(relativePath);
        list.add("@");

        RichYPath copy = new RichYPath(this);
        copy.relativePath = List.copyOf(list);
        return copy;
    }

    @Override
    public Optional getAppend() {
        return Optional.ofNullable(append);
    }

    @Override
    public YPath append(boolean append) {
        RichYPath copy = new RichYPath(this);
        copy.append = append;
        return copy;
    }

    @Override
    public Optional getPrimary() {
        return Optional.ofNullable(primary);
    }

    @Override
    public YPath primary(boolean primary) {
        RichYPath copy = new RichYPath(this);
        copy.primary = primary;
        return copy;
    }

    @Override
    public Optional getForeign() {
        return Optional.ofNullable(foreign);
    }

    @Override
    public YPath foreign(boolean foreign) {
        RichYPath copy = new RichYPath(this);
        copy.foreign = foreign;
        return copy;
    }

    @Override
    public Optional getSchema() {
        return Optional.ofNullable(schema);
    }

    @Override
    public YPath withSchema(YTreeNode schema) {
        RichYPath copy = new RichYPath(this);
        copy.schema = schema;
        return copy;
    }

    @Override
    public List getRanges() {
        return ranges;
    }

    @Override
    public YPath ranges(List ranges) {
        RichYPath copy = new RichYPath(this);
        copy.ranges = List.copyOf(new ArrayList<>(ranges));
        return copy;
    }

    @Override
    public YPath plusRange(RangeCriteria range) {
        List list = new ArrayList<>(ranges);
        list.add(range);

        RichYPath copy = new RichYPath(this);
        copy.ranges = List.copyOf(new ArrayList<>(list));
        return copy;
    }

    @Override
    public List getColumns() {
        return columns;
    }

    @Override
    public YPath withColumns(Collection columns) {
        RichYPath copy = new RichYPath(this);
        copy.columns = List.copyOf(new ArrayList<>(columns));
        return copy;
    }

    @Override
    public Map getRenameColumns() {
        return renameColumns;
    }

    @Override
    public YPath withRenameColumns(Map renameColumns) {
        RichYPath copy = new RichYPath(this);
        copy.renameColumns = Map.copyOf(renameColumns);
        return copy;
    }

    @Override
    public YPath plusRenameColumns(Map renameColumns) {
        Map map = new HashMap<>(this.renameColumns);
        map.putAll(renameColumns);

        RichYPath copy = new RichYPath(this);
        copy.renameColumns = Map.copyOf(map);
        return copy;
    }

    @Override
    public List getSortedBy() {
        return sortedBy;
    }

    @Override
    public YPath sortedBy(List sortedBy) {
        RichYPath copy = new RichYPath(this);
        copy.sortedBy = List.copyOf(new ArrayList<>(sortedBy));
        return copy;
    }

    @Override
    public Optional getTimestamp() {
        return Optional.ofNullable(timestamp);
    }

    @Override
    public YPath withTimestamp(long timestamp) {
        RichYPath copy = new RichYPath(this);
        copy.timestamp = timestamp;
        return copy;
    }

    @Override
    public Optional getFormat() {
        return Optional.ofNullable(format);
    }

    @Override
    public YPath withFormat(String format) {
        RichYPath copy = new RichYPath(this);
        copy.format = format;
        return copy;
    }

    @Override
    public Optional getExecutable() {
        return Optional.ofNullable(executable);
    }

    @Override
    public YPath withExecutable(boolean executable) {
        RichYPath copy = new RichYPath(this);
        copy.executable = executable;
        return copy;
    }

    @Override
    public Optional getCreate() {
        return Optional.ofNullable(create);
    }

    @Override
    public YPath create(boolean create) {
        RichYPath copy = new RichYPath(this);
        copy.create = create;
        return copy;
    }

    @Override
    public Optional getAdditionalAttribute(String attributeName) {
        return Optional.ofNullable(additionalAttributes.get(attributeName));
    }

    @Override
    public Map getAdditionalAttributes() {
        return additionalAttributes;
    }

    @Override
    public YPath withAdditionalAttributes(Map additionalAttributes) {
        RichYPath copy = new RichYPath(this);
        copy.additionalAttributes = Map.copyOf(additionalAttributes);
        return copy;
    }

    @Override
    public YPath plusAdditionalAttribute(String key, YTreeNode value) {
        Map map = new HashMap<>(additionalAttributes);
        map.put(key, value);

        RichYPath copy = new RichYPath(this);
        copy.additionalAttributes = Map.copyOf(map);
        return copy;
    }

    @Override
    public Optional getBypassArtifactCache() {
        return Optional.ofNullable(bypassArtifactCache);
    }

    @Override
    public YPath withBypassArtifactCache(boolean bypassArtifactCache) {
        RichYPath copy = new RichYPath(this);
        copy.bypassArtifactCache = bypassArtifactCache;
        return copy;
    }

    @Override
    public String toString() {
        return toStringImpl(false);
    }

    @Override
    public String toStableString() {
        return toStringImpl(true);
    }

    private String toStringImpl(boolean stable) {
        String simpleString = Stream.concat(Stream.of(rootDesignator), relativePath.stream())
                .collect(Collectors.joining("/"));
        YTreeNode node = toTree();
        if (node.containsAttributes()) {
            String tmp;
            if (stable) {
                tmp = YTreeTextSerializer.stableSerialize(buildAttributes(YTree.builder()).value(31337).build());
            } else {
                tmp = YTreeTextSerializer.serialize(buildAttributes(YTree.builder()).value(31337).build());
            }
            return tmp.substring(0, tmp.length() - 5) + simpleString;
        } else {
            return simpleString;
        }
    }

    @Override
    public YTreeNode toTree() {
        return toTree(YTree.builder()).build();
    }

    private YTreeBuilder buildAttributes(YTreeBuilder builder) {
        return builder
                .beginAttributes()
                .when(append != null, b -> b.key("append").value(append))
                .when(primary != null, b -> b.key("primary").value(primary))
                .when(!ranges.isEmpty(), b -> b.key("ranges").value(ranges, (b2, r) -> {
                    YTreeBuilder rangesBuilder = b2.beginMap();
                    rangesBuilder = r.addRangeCriteria(rangesBuilder);
                    return rangesBuilder.endMap();
                }))
                .when(foreign != null, b -> b.key("foreign").value(foreign))
                .when(!columns.isEmpty(), b -> b.key("columns").value(columns))
                .when(!renameColumns.isEmpty(), b -> {
                    YTreeBuilder mapBuilder = YTree.mapBuilder();
                    for (Map.Entry oldToNewColumn : renameColumns.entrySet()) {
                        mapBuilder.key(oldToNewColumn.getKey()).value(oldToNewColumn.getValue());
                    }
                    return b.key("rename_columns").value(mapBuilder.buildMap());
                })
                .when(!sortedBy.isEmpty(), b -> b.key("sorted_by").value(sortedBy))
                .when(timestamp != null, b -> b.key("timestamp").value(timestamp))
                .when(schema != null, b -> b.key("schema").value(schema))
                .when(format != null, b -> b.key("format").value(format))
                .when(bypassArtifactCache != null,
                        b -> b.key("bypass_artifact_cache").value(bypassArtifactCache))
                .when(executable != null, b -> b.key("executable").value(executable))
                .when(create != null, b -> b.key("create").value(create))
                .apply(b -> {
                    for (Map.Entry node : additionalAttributes.entrySet()) {
                        b.key(node.getKey()).value(node.getValue());
                    }
                    return b;
                })
                .endAttributes();
    }

    @Override
    public YTreeBuilder toTree(YTreeBuilder builder) {
        return buildAttributes(builder)
                .value(Stream.concat(Stream.of(rootDesignator), relativePath.stream())
                        .collect(Collectors.joining("/")));
    }

    public static YPath cypressRoot() {
        return new RichYPath("/", List.of());
    }

    public static YPath objectRoot(GUID id) {
        return new RichYPath("#" + id.toString(), List.of());
    }

    public static YPath simple(String path) {
        return getRootDesignatorAndRelativePath(path);
    }

    public static YPath fromTree(YTreeNode node) {
        String path = node.stringValue();
        Map attributes = node.getAttributes();

        YPath simplePath = getRootDesignatorAndRelativePath(path);
        if (attributes.isEmpty()) {
            return simplePath;
        } else {
            return RichYPath.fromAttributes(simplePath, attributes);
        }
    }

    public static YPath fromString(String data) {
        return RichYPathParser.parse(data);
    }

    private static YPath getRootDesignatorAndRelativePath(String path) {
        if (path.startsWith("/")) {
            return new RichYPath("/", getRelativePath(path, 1));
        } else if (path.startsWith("#")) {
            int ptr = 0;
            while (ptr < path.length() && path.charAt(ptr) != '/') {
                ++ptr;
            }
            String guid = path.substring(1, ptr);
            if (!GUID.isValid(guid)) {
                throw new IllegalArgumentException(path);
            }
            return new RichYPath("#" + guid, getRelativePath(path, ptr));
        } else {
            throw new IllegalArgumentException(path);
        }
    }

    private static List getRelativePath(String path, int ptr) {
        if (ptr >= path.length()) {
            return List.of();
        }
        if (path.charAt(ptr) != '/') {
            throw new IllegalStateException();
        }
        List result = new ArrayList<>();
        while (ptr < path.length()) {
            int nextPtr = ptr + 1;
            while (nextPtr < path.length() && path.charAt(nextPtr) != '/') {
                ++nextPtr;
            }
            if (nextPtr == ptr + 1) {
                throw new IllegalArgumentException(path);
            }
            result.add(path.substring(ptr + 1, nextPtr));
            ptr = nextPtr;
        }
        return List.copyOf(result);
    }

    static YPath fromAttributes(YPath simplePath, Map attrs) {
        Map attributes = new HashMap<>(attrs);

        Optional append = Optional.ofNullable(attributes.remove("append")).map(YTreeNode::boolValue);
        Optional primary = Optional.ofNullable(attributes.remove("primary")).map(YTreeNode::boolValue);
        Optional foreign = Optional.ofNullable(attributes.remove("foreign")).map(YTreeNode::boolValue);
        Optional bypassArtifactCache = Optional.ofNullable(attributes.remove("bypass_artifact_cache"))
                .map(YTreeNode::boolValue);
        Optional executable = Optional.ofNullable(attributes.remove("executable")).map(YTreeNode::boolValue);
        Optional create = Optional.ofNullable(attributes.remove("create")).map(YTreeNode::boolValue);

        List ranges = new ArrayList<>();
        Optional rangesAttribute = Optional.ofNullable(attributes.remove("ranges"));
        if (rangesAttribute.isPresent()) {
            for (YTreeNode range : rangesAttribute.get().listNode()) {
                RangeLimit lower = getLimit(range, "lower_limit");
                RangeLimit upper = getLimit(range, "upper_limit");
                RangeLimit exact = getLimit(range, "exact");

                if (exact != null) {
                    ranges.add(new Exact(exact));
                } else {
                    ranges.add(Range.builder().setLowerLimit(lower).setUpperLimit(upper).build());
                }
            }
        }
        Set columns = Optional.ofNullable(attributes.remove("columns"))
                .map(v -> v.asList().stream()).orElse(Stream.of())
                .map(YTreeNode::stringValue)
                .collect(Collectors.toSet());
        Map renameColumns = Optional.ofNullable(attributes.remove("rename_columns"))
                .map(v -> v.asMap().entrySet().stream()).orElse(Stream.of())
                .collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().stringValue()));
        List sortedBy = Optional.ofNullable(attributes.remove("sorted_by"))
                .map(v -> v.asList().stream()).orElse(Stream.of())
                .map(YTreeNode::stringValue)
                .collect(Collectors.toList());
        Optional timestamp = Optional.ofNullable(attributes.remove("timestamp")).map(YTreeNode::longValue);
        Optional schema = Optional.ofNullable(attributes.remove("schema"));

        YPath result = simplePath;
        if (append.isPresent()) {
            result = result.append(append.get());
        }
        if (primary.isPresent()) {
            result = result.primary(primary.get());
        }
        if (foreign.isPresent()) {
            result = result.foreign(foreign.get());
        }
        if (!ranges.isEmpty()) {
            result = result.ranges(ranges);
        }
        if (!columns.isEmpty()) {
            result = result.withColumns(columns);
        }
        if (!renameColumns.isEmpty()) {
            result = result.withRenameColumns(renameColumns);
        }
        if (!sortedBy.isEmpty()) {
            result = result.sortedBy(sortedBy);
        }
        if (timestamp.isPresent()) {
            result = result.withTimestamp(timestamp.get());
        }
        if (schema.isPresent()) {
            result = result.withSchema(schema.get());
        }
        if (bypassArtifactCache.isPresent()) {
            result = result.withBypassArtifactCache(bypassArtifactCache.get());
        }
        if (executable.isPresent()) {
            result = result.withExecutable(executable.get());
        }
        if (create.isPresent()) {
            result = result.create(create.get());
        }

        if (!attributes.isEmpty()) {
            result = result.withAdditionalAttributes(attributes);
        }
        return result;
    }

    @Nullable
    private static RangeLimit getLimit(YTreeNode node, String key) {
        final YTreeMapNode parentMapNode = node.mapNode();
        if (!parentMapNode.containsKey(key)) {
            return null;
        }
        return RangeLimit.fromTree(parentMapNode.getOrThrow(key));
    }
}

enum TokenType {
    Literal,
    Slash,
    Ampersand,
    At,
    Asterisk,
    StartOfStream,
    EndOfStream,
    Range;

    public static TokenType fromByte(byte sym) {
        switch (sym) {
            case '/':
                return Slash;
            case '@':
                return At;
            case '&':
                return Ampersand;
            case '*':
                return Asterisk;
            default: {
                throw new RuntimeException(String.format("Impossible to convert '%s' to TokenType", sym));
            }
        }
    }
}

@NonNullApi
@NonNullFields
class YPathTokenizer {
    private final byte[] input;
    private TokenType type = TokenType.StartOfStream;
    private TokenType previousType = TokenType.StartOfStream;
    private int tokenLength = 0;
    private int inputShift = 0;
    private ByteArrayOutputStream literalValue = new ByteArrayOutputStream();

    YPathTokenizer(byte[] path) {
        this.input = path;
    }

    TokenType advance() {
        this.inputShift += tokenLength;
        literalValue = new ByteArrayOutputStream();
        if (inputShift == input.length) {
            setType(TokenType.EndOfStream);
            tokenLength = 0;
            return type;
        }

        setType(TokenType.Literal);
        boolean proceed = true;

        int currentIndex = 0;
        while (proceed && inputShift + currentIndex < input.length) {
            byte currentSymbol = input[inputShift + currentIndex];
            var currentToken = YsonTokenType.fromSymbol(currentSymbol);
            if (currentToken == RichYPathTags.BEGIN_COLUMN_SELECTOR_TOKEN
                    || currentToken == RichYPathTags.BEGIN_ROW_SELECTOR_TOKEN) {
                if (currentIndex == 0) {
                    setType(TokenType.Range);
                    currentIndex = input.length - inputShift;
                }
                proceed = false;
                continue;
            }

            switch (currentSymbol) {
                case '/':
                case '@':
                case '&':
                case '*': {
                    if (currentIndex == 0) {
                        this.tokenLength = 1;
                        setType(TokenType.fromByte(currentSymbol));
                        return type;
                    }
                    proceed = false;
                    break;
                }
                case '\\': {
                    currentIndex = advanceEscaped(currentIndex);
                    break;
                }
                default: {
                    literalValue.write(input[inputShift + currentIndex]);
                    ++currentIndex;
                    break;
                }
            }
        }
        tokenLength = currentIndex;
        return type;
    }

    private int advanceEscaped(int currentIndex) {
        if (input[inputShift + currentIndex] != '\\') {
            throw new IllegalStateException("'\\' was expected in advanceEscaped");
        }
        ++currentIndex;
        if (inputShift + currentIndex == input.length) {
            throw new IllegalArgumentException("Unexpected end-of-string in YPath while parsing escape sequence");
        }
        if (isSpecialCharacter(input[inputShift + currentIndex])) {
            literalValue.write(input[inputShift + currentIndex]);
            ++currentIndex;
        } else {
            if (input[inputShift + currentIndex] == 'x') {
                if (currentIndex + 2 >= input.length) {
                    throwMalformedEscapeSequence(ByteBuffer.wrap(input, inputShift + currentIndex - 1, input.length));
                }
                ByteBuffer context = ByteBuffer.wrap(
                        input, inputShift + currentIndex - 1, inputShift + currentIndex + 3);
                int hi = parseHexDigit(input[inputShift + currentIndex + 1], context);
                int lo = parseHexDigit(input[inputShift + currentIndex + 2], context);
                literalValue.write((hi << 4) + lo);
                currentIndex += 3;
            } else {
                throwMalformedEscapeSequence(ByteBuffer.wrap(
                        input, inputShift + currentIndex - 1, inputShift + currentIndex + 1));
            }
        }
        return currentIndex;
    }

    private void throwMalformedEscapeSequence(ByteBuffer context) {
        throw new RuntimeException(String.format("Malformed escape sequence %s in YPath", context));
    }

    private int parseHexDigit(byte ch, ByteBuffer context) {
        if (ch >= '0' && ch <= '9') {
            return ch - '0';
        }

        if (ch >= 'a' && ch <= 'f') {
            return ch - 'a' + 10;
        }

        if (ch >= 'A' && ch <= 'F') {
            return ch - 'A' + 10;
        }

        throwMalformedEscapeSequence(context);
        return 0;
    }

    private boolean isSpecialCharacter(byte ch) {
        return ch == '\\' || ch == '/' || ch == '@' || ch == '*' || ch == '&' || ch == '[' || ch == '{';
    }

    void setType(TokenType type) {
        this.previousType = this.type;
        this.type = type;
    }

    TokenType getType() {
        return type;
    }

    @Nullable
    byte[] getToken() {
        return tokenLength > 0 ? Arrays.copyOfRange(input, inputShift, inputShift + tokenLength) : null;
    }

    byte[] getPrefix() {
        return Arrays.copyOfRange(input, 0, inputShift);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy