com.dgtlrepublic.anitomyj.Token.java Source code

Java tutorial

Introduction

Here is the source code for com.dgtlrepublic.anitomyj.Token.java

Source

/*
 * Copyright (c) 2014-2016, Eren Okka
 * Copyright (c) 2016, Paul Miller
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package com.dgtlrepublic.anitomyj;

import static com.dgtlrepublic.anitomyj.Token.TokenCategory.kBracket;
import static com.dgtlrepublic.anitomyj.Token.TokenCategory.kDelimiter;
import static com.dgtlrepublic.anitomyj.Token.TokenCategory.kIdentifier;
import static com.dgtlrepublic.anitomyj.Token.TokenCategory.kInvalid;
import static com.dgtlrepublic.anitomyj.Token.TokenCategory.kUnknown;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagBracket;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagDelimiter;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagEnclosed;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagIdentifier;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagNotBracket;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagNotDelimiter;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagNotEnclosed;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagNotIdentifier;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagNotUnknown;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagNotValid;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagUnknown;
import static com.dgtlrepublic.anitomyj.Token.TokenFlag.kFlagValid;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

import org.apache.commons.collections.CollectionUtils;

/**
 * An anime filename is tokenized into individual {@link Token}s. This class represents that individual token.
 *
 * @author Paul Miller
 * @author Eren Okka
 */
class Token {
    /** The category of the token */
    enum TokenCategory {
        kUnknown, kBracket, kDelimiter, kIdentifier, kInvalid
    }

    /** TokenFlag, used for searching specific token categories. This allows granular searching of TokenCategories */
    enum TokenFlag {
        /** None */
        kFlagNone,

        /** Categories */
        kFlagBracket, kFlagNotBracket, kFlagDelimiter, kFlagNotDelimiter, kFlagIdentifier, kFlagNotIdentifier, kFlagUnknown, kFlagNotUnknown, kFlagValid, kFlagNotValid,

        /** Enclosed (Enclosed in some bracket (e.g [ ENCLOSED VALUE ] ) */
        kFlagEnclosed, kFlagNotEnclosed
    }

    /** Set of token category flags */
    private static final EnumSet<TokenFlag> kFlagMaskCategories = EnumSet.of(kFlagBracket, kFlagNotBracket,
            kFlagDelimiter, kFlagNotDelimiter, kFlagIdentifier, kFlagNotIdentifier, kFlagUnknown, kFlagNotUnknown,
            kFlagValid, kFlagNotValid);

    /** Set of token enclosed flags */
    private static final EnumSet<TokenFlag> kFlagMaskEnclosed = EnumSet.of(kFlagEnclosed, kFlagNotEnclosed);

    private TokenCategory category;
    private String content;
    private final boolean enclosed;

    /**
     * Constructs a new token.
     *
     * @param category the token category
     * @param content  the token content
     * @param enclosed whether or not the token is enclosed in braces
     */
    public Token(TokenCategory category, String content, boolean enclosed) {
        this.category = category;
        this.content = content;
        this.enclosed = enclosed;
    }

    /** Returns the token category. */
    public TokenCategory getCategory() {
        return category;
    }

    /** Sets the token category. */
    public void setCategory(TokenCategory category) {
        this.category = category;
    }

    /** Returns the token content. */
    public String getContent() {
        return content;
    }

    /** Sets the token content */
    public void setContent(String content) {
        this.content = content;
    }

    /** Returns whether or not the token is enclosed in braces. */
    public boolean isEnclosed() {
        return enclosed;
    }

    /**
     * Validates a token against the {@code flags}. The {@code flags} is used as a search parameter.
     *
     * @param token the token
     * @param flags the flags the token must conform against
     * @return true if the token conforms to the set of {@code flags}; false otherwise
     */
    public static boolean checkTokenFlags(Token token, EnumSet<TokenFlag> flags) {
        /** simple alias to check if flag is a part of the set */
        Function<TokenFlag, Boolean> checkFlag = flags::contains;

        /** make sure token is the correct closure */
        if (CollectionUtils.containsAny(flags, kFlagMaskEnclosed)) {
            boolean success = checkFlag.apply(kFlagEnclosed) == token.enclosed;
            if (!success)
                return false; /** not enclosed correctly (e.g enclosed when we're looking for non-enclosed) */
        }

        /** make sure token is the correct category */
        if (CollectionUtils.containsAny(flags, kFlagMaskCategories)) {
            AtomicBoolean success = new AtomicBoolean(false);
            TriConsumer<TokenFlag, TokenFlag, TokenCategory> checkCategory = (fe, fn, c) -> {
                if (!success.get()) {
                    boolean result = checkFlag.apply(fe) ? token.category == c
                            : checkFlag.apply(fn) && token.category != c;
                    success.set(result);
                }
            };

            checkCategory.accept(kFlagBracket, kFlagNotBracket, kBracket);
            checkCategory.accept(kFlagDelimiter, kFlagNotDelimiter, kDelimiter);
            checkCategory.accept(kFlagIdentifier, kFlagNotIdentifier, kIdentifier);
            checkCategory.accept(kFlagUnknown, kFlagNotUnknown, kUnknown);
            checkCategory.accept(kFlagNotValid, kFlagValid, kInvalid);
            if (!success.get())
                return false;
        }

        return true;
    }

    /**
     * Given a list of {@code tokens}, searches for any token that matches the list of {@code flags}.
     *
     * @param tokens the list of tokens
     * @param begin  the search starting position. <i>Inclusive</i> of {@code begin.pos}.
     * @param flags  the search flags
     * @return the search result
     */
    public static Result findToken(List<Token> tokens, Result begin, TokenFlag... flags) {
        if (begin == null || begin.pos == null)
            return Result.getEmptyResult();
        return findTokenBase(tokens, begin.pos, i -> i < tokens.size(), i -> i + 1, flags);
    }

    /**
     * Given a list of {@code tokens}, searches for any token that matches the list of {@code flags}.
     *
     * @param tokens the list of tokens
     * @param flags  the search flags
     * @return the search result
     */
    public static Result findToken(List<Token> tokens, TokenFlag... flags) {
        return findTokenBase(tokens, 0, i -> i < tokens.size(), i -> i + 1, flags);
    }

    /**
     * Given a list of {@code tokens}, searches for the <i>next</i> token in {@code tokens} that matches the list of
     * {@code flags}.
     *
     * @param tokens   the list of tokens
     * @param position the search starting position. <i>Exclusive</i>.
     * @param flags    the search flags
     * @return the search result
     */
    public static Result findNextToken(List<Token> tokens, int position, TokenFlag... flags) {
        return findTokenBase(tokens, ++position, i -> i < tokens.size(), i -> i + 1, flags);
    }

    /**
     * Given a list of {@code tokens}, searches for the <i>next</i> token in {@code tokens} that matches the list of
     * {@code flags}.
     *
     * @param tokens   the list of tokens
     * @param position the search starting position. <i>Exclusive of position.pos</i>.
     * @param flags    the search flags
     * @return the search result
     */
    public static Result findNextToken(List<Token> tokens, Result position, TokenFlag... flags) {
        return findTokenBase(tokens, position.pos + 1, i -> i < tokens.size(), i -> i + 1, flags);
    }

    /**
     * Given a list of {@code tokens}, searches for the <i>previous</i> token in {@code tokens} that matches the list of
     * {@code flags}.
     *
     * @param tokens   the list of tokens
     * @param position the search starting position. <i>Exclusive</i>.
     * @param flags    the search flags
     * @return the search result
     */
    public static Result findPrevToken(List<Token> tokens, int position, TokenFlag... flags) {
        return findTokenBase(tokens, --position, i -> i >= 0, i -> i - 1, flags);
    }

    /**
     * Given a list of {@code tokens}, searches for the <i>previous</i> token in {@code tokens} that matches the list of
     * {@code flags}.
     *
     * @param tokens   the list of tokens
     * @param position the search starting position. <i>Exclusive of position.pos</i>.
     * @param flags    the search flags
     * @return the search result
     */
    public static Result findPrevToken(List<Token> tokens, Result position, TokenFlag... flags) {
        return findTokenBase(tokens, position.pos - 1, i -> i >= 0, i -> i - 1, flags);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (!(o instanceof Token))
            return false;
        Token token = (Token) o;
        return enclosed == token.enclosed && category == token.category && Objects.equals(content, token.content);
    }

    @Override
    public int hashCode() {
        return Objects.hash(category, content, enclosed);
    }

    @Override
    public String toString() {
        return "Token{" + "category=" + category + ", content='" + content + '\'' + ", enclosed=" + enclosed + '}';
    }

    /************ P R I V A T E  A P I ********** */

    /**
     * Given a list of tokens finds the first token the passes {@link #checkTokenFlags(Token, EnumSet)}.
     *
     * @param tokens         the list of the tokens to search
     * @param startIdx       the start index of the search. Inclusive.
     * @param shouldContinue a function that returns whether or not we should continue searching
     * @param next           a function that returns the next search index
     * @param flags          the flags the each token should be validated against
     * @return the found token
     */
    private static Result findTokenBase(List<Token> tokens, int startIdx, Function<Integer, Boolean> shouldContinue,
            Function<Integer, Integer> next, TokenFlag... flags) {
        EnumSet<TokenFlag> find = EnumSet.noneOf(TokenFlag.class);
        find.addAll(Arrays.asList(flags));

        for (int i = startIdx; shouldContinue.apply(i); i = next.apply(i)) {
            Token token = tokens.get(i);
            if (checkTokenFlags(token, find)) {
                return new Result(token, i);
            }
        }

        return new Result(null, null);
    }

    /** Search result for finds */
    public static class Result {
        public Token token;

        public Integer pos;

        /**
         * Constructs a new search result.
         *
         * @param token       the found token
         * @param searchIndex the index the token was found
         */
        public Result(Token token, Integer searchIndex) {
            this.token = token;
            this.pos = searchIndex;
        }

        /** Returns an empty search result. */
        public static Result getEmptyResult() {
            return new Result(null, null);
        }

        @Override
        public String toString() {
            return "Result{" + "token=" + token + ", pos=" + pos + '}';
        }
    }
}