001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.tools.formatting.template.lexer;
017
018import static de.cuioss.tools.base.Preconditions.checkArgument;
019import static de.cuioss.tools.collect.CollectionLiterals.immutableList;
020import static de.cuioss.tools.string.MoreStrings.isEmpty;
021import static java.util.Objects.requireNonNull;
022
023import java.io.Serializable;
024import java.util.List;
025
026import de.cuioss.tools.formatting.template.FormatterSupport;
027import de.cuioss.tools.formatting.template.token.Token;
028import lombok.AccessLevel;
029import lombok.EqualsAndHashCode;
030import lombok.Getter;
031import lombok.ToString;
032
033/**
034 * Functionality of scanning plain text and split this to tokens
035 *
036 * @param <T> bounds lexer to one type to avoid cross using accidentally
037 * @author Eugen Fischer
038 */
039@ToString
040@EqualsAndHashCode
041public abstract class Lexer<T extends FormatterSupport> implements Serializable {
042
043    private static final long serialVersionUID = 8645233576605974741L;
044
045    @Getter(AccessLevel.PROTECTED)
046    private final List<String> tokenList;
047
048    /**
049     * Constructor of Lexer. Source provide information of "tokens" which he
050     * supports. Therefore {@link FormatterSupport#getSupportedPropertyNames()} will
051     * be used.
052     *
053     * @param source must not be null
054     * @throws NullPointerException     if source is missing
055     * @throws IllegalArgumentException if attribute is null or empty
056     */
057    protected Lexer(final T source) {
058        requireNonNull(source, "Source must not be null");
059        tokenList = immutableList(requireNonNull(source.getSupportedPropertyNames()));
060        for (final String attribute : tokenList) {
061            checkArgument(!isEmpty(attribute), "Attributes must not be null or empty. '" + tokenList + "'");
062        }
063    }
064
065    /**
066     * Throw IllegalArgumentException with information about wrong token and
067     * supported tokens
068     *
069     * @param wrongToken    must not be null
070     * @param allowedTokens must not be null
071     */
072    protected static final void throwUnsupportedTokenException(final String wrongToken,
073            final List<String> allowedTokens) {
074        final var builder = new StringBuilder();
075        builder.append("Unsupported token '").append(wrongToken).append("' was detected.\n").append("Allowed are :\n");
076        for (final String allowedToken : allowedTokens) {
077            builder.append(" - ").append(allowedToken).append("\n");
078        }
079        throw new IllegalArgumentException(builder.toString());
080    }
081
082    /**
083     * Parse template into Token List according attribute list
084     *
085     * @param input template string
086     * @return created list of token, list could be empty if input template is null
087     *         or empty
088     * @throws IllegalArgumentException if template include unknown token, or
089     *                                  doesn't fit the rules
090     */
091    public abstract List<Token> scan(final String input);
092
093    /**
094     * Validate template by scan this
095     *
096     * @param input to be validated
097     */
098    public final void validateTemplate(final String input) {
099        scan(input);
100    }
101
102    /**
103     * Supported expression language
104     */
105    public enum ExpressionLanguage {
106        /** {@code [attribute1][attribute2]..[attribute n]} */
107        SIMPLE_SQUARED_BRACKTES,
108        /** {attribute1}{attribute2}..{attribute n} */
109        SIMPLE_CURLY_BRACKETS,
110        /** {@code <attribute1><attribute2>..<attribute n>} */
111        SIMPLE_ANGLE_BRACKET,
112        /**
113         * usage of String Template Expression Language
114         *
115         * @see <a href=
116         *      "http://www.antlr.org/wiki/display/ST/StringTemplate+3+Documentation">
117         *      Documentation</a>
118         */
119        STEL
120    }
121
122}