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}