001package io.jstach.jstachio; 002 003import java.io.IOException; 004import java.io.UncheckedIOException; 005import java.util.function.Function; 006 007import org.eclipse.jdt.annotation.Nullable; 008 009import io.jstach.jstache.JStacheFormatter; 010import io.jstach.jstache.JStacheFormatterTypes; 011 012/** 013 * Formats and then sends the results to the downstream appender. 014 * 015 * Implementations should be singleton like and should not contain state. By default 016 * native types are passed straight through to the downstream appender. If this is not 017 * desired one can override those methods. 018 * <p> 019 * <em>Important: the formatter does not decide what types are allowed at compile time to 020 * be formatted.</em> To control what types are allowed to be formatted see 021 * {@link JStacheFormatterTypes}. 022 * <p> 023 * An alternative to implementing this complicated interface is to simply make a 024 * {@code Function<@Nullable Object, String>} and call {@link #of(Function)} to create a 025 * formatter. 026 * 027 * @apiNote Although the formatter has access to the raw {@link Appendable} the formatter 028 * should never use it directly and simply pass it on to the downstream appender. 029 * @author agentgt 030 * @see JStacheFormatterTypes 031 * @see JStacheFormatter 032 * 033 */ 034public interface Formatter extends Function<@Nullable Object, String> { 035 036 /** 037 * Formats an object by using {@link StringBuilder} and calling 038 * {@link #format(Appender, Appendable, String, Class, Object)}. 039 * @param t the object to be formatted. Maybe <code>null</code>. 040 * @return the formatted results as a String. 041 */ 042 @Override 043 default String apply(@Nullable Object t) { 044 StringBuilder sb = new StringBuilder(); 045 try { 046 format(Appender.stringAppender(), sb, "", Object.class, t); 047 } 048 catch (IOException e) { 049 throw new UncheckedIOException(e); 050 } 051 return sb.toString(); 052 } 053 054 /** 055 * Formats the object and then sends the results to the downstream appender. 056 * 057 * 058 * @apiNote Although the formatter has access to the raw {@link Appendable} the 059 * formatter should never use it directly and simply pass it on to the downstream 060 * appender. 061 * @param <A> the appendable type 062 * @param <APPENDER> the downstream appender type 063 * @param downstream the downstream appender to be used instead of the appendable 064 * directly 065 * @param a the appendable to be passed to the appender 066 * @param path the dotted mustache like path 067 * @param c the object class but is not guaranteed to be accurate. If it is not known 068 * Object.class will be used. 069 * @param o the object which maybe null 070 * @throws IOException if the appender or appendable throws an exception 071 */ 072 <A extends Appendable, APPENDER extends Appender<A>> // 073 void format(APPENDER downstream, A a, String path, Class<?> c, @Nullable Object o) throws IOException; 074 075 /** 076 * Formats the object and then sends the results to the downstream appender. The 077 * default implementation passes natives through to the downstream appender. 078 * 079 * @apiNote Although the formatter has access to the raw {@link Appendable} the 080 * formatter should never use it directly and simply pass it on to the downstream 081 * appender. 082 * @param <A> the appendable type 083 * @param <APPENDER> the downstream appender type 084 * @param downstream the downstream appender to be used instead of the appendable 085 * directly 086 * @param a the appendable to be passed to the appender 087 * @param path the dotted mustache like path 088 * @param c character 089 * @throws IOException if the appender or appendable throws an exception 090 */ 091 default <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 092 char c) throws IOException { 093 downstream.append(a, c); 094 } 095 096 /** 097 * Formats the object and then sends the results to the downstream appender. The 098 * default implementation passes natives through to the downstream appender. 099 * 100 * @apiNote Although the formatter has access to the raw {@link Appendable} the 101 * formatter should never use it directly and simply pass it on to the downstream 102 * appender. 103 * @param <A> the appendable type 104 * @param <APPENDER> the downstream appender type 105 * @param downstream the downstream appender to be used instead of the appendable 106 * directly 107 * @param a the appendable to be passed to the appender 108 * @param path the dotted mustache like path 109 * @param s short 110 * @throws IOException if the appender or appendable throws an exception 111 */ 112 default <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 113 short s) throws IOException { 114 downstream.append(a, s); 115 } 116 117 /** 118 * Formats the object and then sends the results to the downstream appender. The 119 * default implementation passes natives through to the downstream appender. 120 * 121 * @apiNote Although the formatter has access to the raw {@link Appendable} the 122 * formatter should never use it directly and simply pass it on to the downstream 123 * appender. 124 * @param <A> the appendable type 125 * @param <APPENDER> the downstream appender type 126 * @param downstream the downstream appender to be used instead of the appendable 127 * directly 128 * @param a the appendable to be passed to the appender 129 * @param path the dotted mustache like path 130 * @param i integer 131 * @throws IOException if the appender or appendable throws an exception 132 */ 133 default <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 134 int i) throws IOException { 135 downstream.append(a, i); 136 } 137 138 /** 139 * Formats the object and then sends the results to the downstream appender. The 140 * default implementation passes natives through to the downstream appender. 141 * 142 * @apiNote Although the formatter has access to the raw {@link Appendable} the 143 * formatter should never use it directly and simply pass it on to the downstream 144 * appender. 145 * @param <A> the appendable type 146 * @param <APPENDER> the downstream appender type 147 * @param downstream the downstream appender to be used instead of the appendable 148 * directly 149 * @param a the appendable to be passed to the appender 150 * @param path the dotted mustache like path 151 * @param l long 152 * @throws IOException if the appender or appendable throws an exception 153 */ 154 default <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 155 long l) throws IOException { 156 downstream.append(a, l); 157 } 158 159 /** 160 * Formats the object and then sends the results to the downstream appender. The 161 * default implementation passes natives through to the downstream appender. 162 * 163 * @apiNote Although the formatter has access to the raw {@link Appendable} the 164 * formatter should never use it directly and simply pass it on to the downstream 165 * appender. 166 * @param <A> the appendable type 167 * @param <APPENDER> the downstream appender type 168 * @param downstream the downstream appender to be used instead of the appendable 169 * directly 170 * @param a the appendable to be passed to the appender 171 * @param path the dotted mustache like path 172 * @param d double 173 * @throws IOException if the appender or appendable throws an exception 174 */ 175 default <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 176 double d) throws IOException { 177 downstream.append(a, d); 178 } 179 180 /** 181 * Formats the object and then sends the results to the downstream appender. The 182 * default implementation passes natives through to the downstream appender. 183 * 184 * @apiNote Although the formatter has access to the raw {@link Appendable} the 185 * formatter should never use it directly and simply pass it on to the downstream 186 * appender. 187 * @param <A> the appendable type 188 * @param <APPENDER> the downstream appender type 189 * @param downstream the downstream appender to be used instead of the appendable 190 * directly 191 * @param a the appendable to be passed to the appender 192 * @param path the dotted mustache like path 193 * @param b boolean 194 * @throws IOException if the appender or appendable throws an exception 195 */ 196 default <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 197 boolean b) throws IOException { 198 downstream.append(a, b); 199 } 200 201 /** 202 * Formats the object and then sends the results to the downstream appender. The 203 * default implementation passes natives through to the downstream appender. 204 * 205 * @apiNote Although the formatter has access to the raw {@link Appendable} the 206 * formatter should never use it directly and simply pass it on to the downstream 207 * appender. 208 * @param <A> the appendable type 209 * @param <APPENDER> the downstream appender type 210 * @param downstream the downstream appender to be used instead of the appendable 211 * directly 212 * @param a the appendable to be passed to the appender 213 * @param path the dotted mustache like path 214 * @param s String 215 * @throws IOException if the appender or appendable throws an exception 216 */ 217 default <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 218 String s) throws IOException { 219 downstream.append(a, s); 220 } 221 222 /** 223 * Adapts a function to a formatter. 224 * 225 * If the function is already a formatter then it is simply returned (noop). Thus it 226 * is safe to repeatedly call this on formatters. If the function is adapted the 227 * returned adapted formatter does not pass native types to the inputted function. 228 * @param formatterFunction if it is already an escaper 229 * @return adapted formattter 230 */ 231 public static Formatter of(@SuppressWarnings("exports") Function<@Nullable Object, String> formatterFunction) { 232 if (formatterFunction instanceof Formatter f) { 233 return f; 234 } 235 return new ObjectFunctionFormatter(formatterFunction); 236 } 237 238} 239 240class ObjectFunctionFormatter implements Formatter { 241 242 private final Function<@Nullable Object, String> function; 243 244 public ObjectFunctionFormatter(Function<@Nullable Object, String> function) { 245 super(); 246 this.function = function; 247 } 248 249 @Override 250 public <A extends Appendable, APPENDER extends Appender<A>> void format(APPENDER downstream, A a, String path, 251 Class<?> c, @Nullable Object o) throws IOException { 252 String result = function.apply(o); 253 downstream.append(a, result); 254 } 255 256}