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;
017
018import static de.cuioss.tools.collect.MoreCollections.isEmpty;
019import static de.cuioss.tools.string.MoreStrings.emptyToNull;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.List;
024
025import lombok.Data;
026import lombok.EqualsAndHashCode;
027import lombok.NonNull;
028import lombok.ToString;
029
030/**
031 * Provide concatenation of strings by using
032 * {@linkplain String#join(CharSequence, CharSequence...)}. Furthermore,
033 * formatter supports different strategies for values handling. (see
034 * {@link ValueHandling})
035 *
036 * @author Eugen Fischer
037 */
038@Data
039public class SimpleFormatter implements Serializable {
040
041    /**
042     *
043     */
044    public enum ValueHandling {
045        /**
046         * Format all available data. If some are null or empty skip them silently.
047         */
048        FORMAT_IF_ANY_AVAILABLE,
049        /**
050         * Format all available data. If one of them is null or empty skip all silently.
051         */
052        FORMAT_IF_ALL_AVAILABLE
053    }
054
055    /**
056     * serial version UID
057     */
058    private static final long serialVersionUID = -4761082365099064435L;
059
060    private final String separator;
061
062    private final ValueHandling handling;
063
064    /**
065     * Concatenate values by separator and return result inside the parentheses.
066     * Handle parameter according defined ValueHandling strategy.
067     *
068     * @param values ellipses of string values
069     *
070     * @return {@code null} if nothing to put in parentheses
071     */
072    public String formatParentheses(final String... values) {
073        return format(cleanUp(values));
074    }
075
076    /**
077     * Concatenate values by separator according defined ValueHandling strategy.
078     *
079     * @param values ellipses of string values
080     *
081     * @return {@code null} if nothing to concatenate
082     */
083    public String format(final String... values) {
084        return getJoined(cleanUp(values));
085    }
086
087    private List<String> cleanUp(final String... values) {
088        final List<String> result = new ArrayList<>(0);
089        if (null != values) {
090            for (final String item : values) {
091                final var value = emptyToNull(item);
092                if (null == value) {
093                    if (ValueHandling.FORMAT_IF_ALL_AVAILABLE.equals(handling)) {
094                        result.clear();
095                        break;
096                    }
097                } else {
098                    result.add(value);
099                }
100            }
101        }
102        return result;
103    }
104
105    private String getJoined(final List<String> values) {
106        if (isEmpty(values)) {
107            return null;
108        }
109        var filtered = values.stream().filter(element -> !isEmpty(element)).toList();
110        if (isEmpty(filtered)) {
111            return null;
112        }
113        return String.join(separator, filtered);
114    }
115
116    private String format(final List<String> values) {
117        final var joined = getJoined(values);
118        if (null != joined) {
119            return "(%s)".formatted(joined);
120        }
121        return null;
122    }
123
124    /**
125     * @return a newly created instance of {@link SimpleFormatterBuilder}
126     */
127    public static SimpleFormatterBuilder builder() {
128        return new SimpleFormatterBuilder();
129    }
130
131    /**
132     * Internal Builder representation
133     */
134    @ToString
135    @EqualsAndHashCode
136    public static class SimpleFormatterBuilder implements Serializable {
137
138        private static final long serialVersionUID = 6414005370772800008L;
139
140        /**
141         * Use {@linkplain ValueHandling#FORMAT_IF_ALL_AVAILABLE} as value handling
142         * strategy
143         *
144         * @return initialized {@link BuilderWithStrategy} with defined value handling
145         *         strategy
146         */
147        public BuilderWithStrategy skipResultIfAnyValueIsMissing() {
148            return new BuilderWithStrategy(ValueHandling.FORMAT_IF_ALL_AVAILABLE);
149        }
150
151        /**
152         * Use {@linkplain ValueHandling#FORMAT_IF_ANY_AVAILABLE} as value handling
153         * strategy
154         *
155         * @return initialized {@link BuilderWithStrategy} with defined value handling
156         *         strategy
157         */
158        public BuilderWithStrategy ignoreMissingValues() {
159            return new BuilderWithStrategy(ValueHandling.FORMAT_IF_ANY_AVAILABLE);
160        }
161
162        /**
163         * Internal Builder representation incorporating a strategy
164         */
165        @ToString
166        @EqualsAndHashCode
167        public static class BuilderWithStrategy implements Serializable {
168
169            private static final long serialVersionUID = -1987354973684803562L;
170
171            private final ValueHandling valueHandlingStrategy;
172
173            protected BuilderWithStrategy(final ValueHandling strategy) {
174                valueHandlingStrategy = strategy;
175            }
176
177            /**
178             * Create SimpleFormatter
179             *
180             * @param separator must not be null
181             *
182             * @return {@link SimpleFormatter} with defined value handling strategy and
183             *         separator
184             */
185            public SimpleFormatter separatesBy(@NonNull final String separator) {
186                return new SimpleFormatter(separator, valueHandlingStrategy);
187            }
188        }
189
190    }
191}