001package io.jstach.jstachio;
002
003import java.io.IOException;
004
005import org.eclipse.jdt.annotation.NonNull;
006import org.eclipse.jdt.annotation.Nullable;
007
008/**
009 * A singleton like decorator for appendables that has additional methods for dealing with
010 * native types.
011 *
012 * @apiNote Unlike an Appendable this class is expected to be reused so avoid state and
013 * implementations should be thread safe.
014 * @author agentgt
015 * @param <A> the appendable
016 * @see Escaper
017 */
018public interface Appender<A extends Appendable> {
019
020        /**
021         * Analogous to {@link Appendable#append(CharSequence)}.
022         * @param a appendable to write to. Always non null.
023         * @param s unlike appendable always non null.
024         * @throws IOException if an error happens while writting to the appendable
025         */
026        public void append(A a, CharSequence s) throws IOException;
027
028        /**
029         * Analogous to {@link Appendable#append(CharSequence, int, int)}.
030         * @param a appendable to write to. Never null.
031         * @param csq Unlike appendable never null.
032         * @param start start inclusive
033         * @param end end exclusive
034         * @throws IOException if an error happens while writting to the appendable
035         */
036        public void append(A a, CharSequence csq, int start, int end) throws IOException;
037
038        /**
039         * Appends a character to an appendable.
040         * @param a appendable to write to. Never null.
041         * @param c character
042         * @throws IOException if an error happens while writting to the appendable
043         */
044        public void append(A a, char c) throws IOException;
045
046        /**
047         * Write a short by using {@link String#valueOf(int)}
048         * @param a appendable to write to. Never null.
049         * @param s short
050         * @throws IOException if an error happens while writting to the appendable
051         */
052        default void append(A a, short s) throws IOException {
053                append(a, String.valueOf(s));
054        }
055
056        /**
057         * Write a int by using {@link String#valueOf(int)}.
058         * <p>
059         * Implementations should override if they want different behavior or able to support
060         * appendables that can write the native type.
061         * @param a appendable to write to. Never null.
062         * @param i int
063         * @throws IOException if an error happens while writting to the appendable
064         */
065        default void append(A a, int i) throws IOException {
066                append(a, String.valueOf(i));
067        }
068
069        /**
070         * Write a long by using {@link String#valueOf(long)}.
071         * <p>
072         * Implementations should override if they want different behavior or able to support
073         * appendables that can write the native type.
074         * @param a appendable to write to. Never null.
075         * @param l long
076         * @throws IOException if an error happens while writting to the appendable
077         */
078        default void append(A a, long l) throws IOException {
079                append(a, String.valueOf(l));
080        }
081
082        /**
083         * Write a long by using {@link String#valueOf(long)}.
084         * <p>
085         * Implementations should override if they want different behavior or able to support
086         * appendables that can write the native type.
087         * @param a appendable to write to. Never null.
088         * @param d double
089         * @throws IOException if an error happens while writting to the appendable
090         */
091        default void append(A a, double d) throws IOException {
092                append(a, String.valueOf(d));
093        }
094
095        /**
096         * Write a long by using {@link String#valueOf(long)}.
097         * <p>
098         * Implementations should override if they want different behavior or able to support
099         * appendables that can write the native type.
100         * @param a appendable to write to. Never null.
101         * @param b boolean
102         * @throws IOException if an error happens while writting to the appendable
103         */
104        default void append(A a, boolean b) throws IOException {
105                append(a, String.valueOf(b));
106        }
107
108        /**
109         * Decorates an appendable with this appender such that the returned appendable will
110         * call the this appender which will then write to the inputted appendable.
111         * @param appendable never null.
112         * @return Appendable never null.
113         */
114        default Appendable toAppendable(A appendable) {
115                return new AppenderAppendable<>(this, appendable);
116        }
117
118        /**
119         * Default appender simply passes the contents unchanged to the Appendable.
120         * @return a passthrough appender
121         */
122        public static Appender<Appendable> defaultAppender() {
123                return DefaultAppender.INSTANCE;
124        }
125
126        /**
127         * An appender that will directly call StringBuilder methods for native types.
128         * <p>
129         * This is a low level utility appenrer for where performance matters.
130         * @return an appender specifically for {@link StringBuilder}
131         */
132        public static Appender<StringBuilder> stringAppender() {
133                return StringAppender.INSTANCE;
134        }
135
136}
137
138/**
139 * Default appender simply passes the contents unchanged to the Appendable.
140 * @author agentgt
141 *
142 */
143enum DefaultAppender implements Appender<Appendable> {
144
145        /**
146         * Singleton instance
147         */
148        INSTANCE;
149
150        @Override
151        public void append(Appendable a, CharSequence s) throws IOException {
152                a.append(s);
153        }
154
155        @Override
156        public void append(Appendable a, CharSequence csq, int start, int end) throws IOException {
157                a.append(csq, start, end);
158        }
159
160        @Override
161        public void append(Appendable a, char c) throws IOException {
162                a.append(c);
163        }
164
165}
166
167/**
168 * An appender that will directly call StringBuilder methods for native types.
169 * <p>
170 * This is a low level utility class for where performance matters.
171 *
172 * @author agentgt
173 *
174 */
175enum StringAppender implements Appender<StringBuilder> {
176
177        /**
178         * Singleton instance
179         */
180        INSTANCE;
181
182        /**
183         * {@inheritDoc}
184         */
185        @Override
186        public void append(StringBuilder a, CharSequence s) throws IOException {
187                a.append(s);
188        }
189
190        /**
191         * {@inheritDoc}
192         */
193        @Override
194        public void append(StringBuilder a, CharSequence csq, int start, int end) throws IOException {
195                a.append(csq, start, end);
196        }
197
198        /**
199         * {@inheritDoc}
200         */
201        @Override
202        public void append(StringBuilder a, char c) throws IOException {
203                a.append(c);
204        }
205
206        /**
207         * {@inheritDoc}
208         */
209        @Override
210        public void append(StringBuilder a, short s) throws IOException {
211                a.append(s);
212        }
213
214        /**
215         * {@inheritDoc}
216         */
217        public void append(StringBuilder a, int i) throws IOException {
218                a.append(i);
219        }
220
221        /**
222         * {@inheritDoc}
223         */
224        public void append(StringBuilder a, long l) throws IOException {
225                a.append(l);
226        }
227
228        /**
229         * {@inheritDoc}
230         */
231        public void append(StringBuilder a, double d) throws IOException {
232                a.append(d);
233        }
234
235        /**
236         * {@inheritDoc}
237         */
238        public void append(StringBuilder a, boolean b) throws IOException {
239                a.append(b);
240        }
241
242}
243
244class AppenderAppendable<A extends Appendable> implements Appendable {
245
246        private final Appender<A> appender;
247
248        private final A appendable;
249
250        public AppenderAppendable(Appender<A> appender, A appendable) {
251                super();
252                this.appender = appender;
253                this.appendable = appendable;
254        }
255
256        @Override
257        public @NonNull Appendable append(@Nullable CharSequence csq) throws @Nullable IOException {
258                appender.append(appendable, csq);
259                return this;
260        }
261
262        @Override
263        public @NonNull Appendable append(@Nullable CharSequence csq, int start, int end) throws IOException {
264                appender.append(appendable, csq, start, end);
265                return this;
266        }
267
268        @Override
269        public @NonNull Appendable append(char c) throws IOException {
270                appender.append(appendable, c);
271                return this;
272        }
273
274}