001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.serializer; 018 019import java.io.*; 020import java.net.*; 021import java.util.*; 022 023import org.apache.juneau.*; 024import org.apache.juneau.internal.*; 025 026/** 027 * Simple wrapper around a standard {@link Writer} with additional methods. 028 * 029 * <p> 030 * Modeled after the Java ProcessBuilder class so that you can chain commands to reduce the need for string 031 * concatenation for performance reasons. 032 * 033 * <h5 class='section'>Example:</h5> 034 * <p class='bjava'> 035 * <jv>writer</jv>.append(<js>"foo"</js>).nl().i(5).append(<js>"bar"</js>); 036 * </p> 037 * 038 * <h5 class='section'>See Also:</h5><ul> 039 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a> 040 * </ul> 041 */ 042public class SerializerWriter extends Writer { 043 044 /** The underlying writer. */ 045 protected final Writer out; 046 047 /** Use-whitespace flag. */ 048 protected final boolean useWhitespace; 049 050 /** Max indentation levels. */ 051 protected final int maxIndent; 052 053 /** Trim strings flag. */ 054 protected final boolean trimStrings; 055 056 /** The quote character being used by this writer. */ 057 protected final char quoteChar; 058 059 /** The URI resolver of the request. */ 060 protected final UriResolver uriResolver; 061 062 /** 063 * @param out The writer being wrapped. 064 * @param useWhitespace 065 * If <jk>true</jk>, calling {@link #cr(int)} will create an indentation and calling {@link #s()} will write a 066 * space character. 067 * @param maxIndent The maximum indentation level. 068 * @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized. 069 * @param quoteChar The character to write when {@link #q()} is called. 070 * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form. 071 */ 072 public SerializerWriter(Writer out, boolean useWhitespace, int maxIndent, boolean trimStrings, char quoteChar, UriResolver uriResolver) { 073 this.out = out; 074 this.useWhitespace = useWhitespace; 075 this.maxIndent = maxIndent; 076 this.trimStrings = trimStrings; 077 this.quoteChar = quoteChar; 078 this.uriResolver = uriResolver; 079 } 080 081 /** 082 * Copy Constructor 083 * 084 * @param w Writer being copied. 085 */ 086 public SerializerWriter(SerializerWriter w) { 087 this.out = w.out; 088 this.useWhitespace = w.useWhitespace; 089 this.maxIndent = w.maxIndent; 090 this.trimStrings = w.trimStrings; 091 this.quoteChar = w.quoteChar; 092 this.uriResolver = w.uriResolver; 093 } 094 095 /** 096 * Performs a carriage return. 097 * 098 * <p> 099 * Adds a newline and the specified number of tabs (if the {@code useWhitespace} setting is enabled) to the output. 100 * 101 * @param depth The indentation. 102 * @return This object. 103 */ 104 public SerializerWriter cr(int depth) { 105 if (useWhitespace && depth <= maxIndent) 106 return nl(depth).i(depth); 107 return this; 108 } 109 110 /** 111 * Performs a carriage return at the end of a line. 112 * 113 * <p> 114 * Adds a newline and the specified number of tabs (if the {@code useWhitespace} setting is enabled) to the output. 115 * 116 * @param depth The indentation. 117 * @return This object. 118 */ 119 public SerializerWriter cre(int depth) { 120 if (useWhitespace && depth <= maxIndent-1) 121 return nl(depth).i(depth); 122 return this; 123 } 124 125 /** 126 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text, followed by a newline 127 * (if the {@code useWhitespace} setting is enabled). 128 * 129 * @param indent The number of tabs to indent. 130 * @param value The text to write. 131 * @return This object. 132 */ 133 public SerializerWriter appendln(int indent, String value) { 134 return append(indent, true, value); 135 } 136 137 /** 138 * Writes the specified text followed by a newline (if the {@code useWhitespace} setting is enabled). 139 * 140 * @param value The text to write. 141 * @return This object. 142 */ 143 public SerializerWriter appendln(String value) { 144 return append(0, true, value); 145 } 146 147 /** 148 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text. 149 * 150 * @param indent The number of tabs to indent. 151 * @param value The text to write. 152 * @return This object. 153 */ 154 public SerializerWriter append(int indent, String value) { 155 return append(indent, false, value); 156 } 157 158 /** 159 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text. 160 * 161 * @param indent The number of tabs to indent. 162 * @param value The character to write. 163 * @return This object. 164 */ 165 public SerializerWriter append(int indent, char value) { 166 return i(indent).w(value); 167 } 168 169 /** 170 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text, optionally followed by a 171 * newline (if the {@code useWhitespace} setting is enabled). 172 * 173 * @param indent The number of tabs to indent. 174 * @param newline If <jk>true</jk>, then a newline is written. 175 * @param value The text to write. 176 * @throws IOException If a problem occurred trying to write to the writer. 177 * @return This object. 178 */ 179 private SerializerWriter append(int indent, boolean newline, String value) { 180 181 if (value == null) 182 return this; 183 184 // If text contains newlines, we break it up into lines and indent them separately. 185 if (value.indexOf('\n') != -1 && useWhitespace && indent <= maxIndent) { 186 for (StringTokenizer st = new StringTokenizer(value, "\n"); st.hasMoreTokens();) 187 i(indent).w(st.nextToken()).w("\n"); 188 } else { 189 i(indent).w(value); 190 } 191 192 if (newline) 193 nl(indent); 194 195 return this; 196 } 197 198 /** 199 * Appends the specified object as a URI. 200 * 201 * <p> 202 * Object is converted to a <c>String</c> using <c>toString()</c>, so this will work on {@link URL} or 203 * {@link URI} objects, or any other type that returns a URI via it's <c>toString()</c> method. 204 * 205 * <p> 206 * The URI is resolved based on the {@link Serializer.Builder#uriRelativity(UriRelativity)} and 207 * {@link Serializer.Builder#uriResolution(UriResolution)} settings and the {@link UriContext} that's part of the 208 * session. 209 * 210 * @param value The URI to serialize. 211 * @return This object. 212 */ 213 public SerializerWriter appendUri(Object value) { 214 uriResolver.append(this, value); 215 return this; 216 } 217 218 /** 219 * Appends the specified characters to this writer. 220 * 221 * @param value The characters to append to this writer. 222 * @return This object. 223 */ 224 public SerializerWriter append(char[] value) { 225 for (char c : value) 226 w(c); 227 return this; 228 } 229 230 /** 231 * Adds a whitespace character to the output if the {@code useWhitespace} setting is enabled. 232 * 233 * @return This object. 234 */ 235 public SerializerWriter s() { 236 if (useWhitespace) 237 w(' '); 238 return this; 239 } 240 241 /** 242 * Adds the quote character specified by the {@code quoteChar} setting to the output. 243 * 244 * @return This object. 245 */ 246 public SerializerWriter q() { 247 w(quoteChar); 248 return this; 249 } 250 251 /** 252 * Writes an indent to the writer if the {@code useWhitespace} setting is enabled. 253 * 254 * @param indent The number of tabs to indent. 255 * @return This object. 256 */ 257 public SerializerWriter i(int indent) { 258 if (useWhitespace && indent <= maxIndent) 259 for (int i = 0; i < indent; i++) 260 w('\t'); 261 return this; 262 } 263 264 /** 265 * Writes an end-of-line indent to the writer if the {@code useWhitespace} setting is enabled. 266 * 267 * @param indent The number of tabs to indent. 268 * @return This object. 269 */ 270 public SerializerWriter ie(int indent) { 271 if (useWhitespace && indent <= maxIndent-1) 272 for (int i = 0; i < indent; i++) 273 w('\t'); 274 return this; 275 } 276 277 /** 278 * Writes a newline to the writer if the {@code useWhitespace} setting is enabled. 279 * 280 * @param indent The current indentation level. 281 * @return This object. 282 */ 283 public SerializerWriter nl(int indent) { 284 if (useWhitespace && indent <= maxIndent) 285 w('\n'); 286 return this; 287 } 288 289 /** 290 * Writes a space if the boolean expression is <jk>true</jk> and {@code useWhitespace} is false. 291 * 292 * <p> 293 * Intended for cases in XML where text should be separated by either a space or newline. 294 * This ensures the text is separated by a space if whitespace is disabled. 295 * 296 * @param flag The boolean flag. 297 * @return This object. 298 */ 299 public SerializerWriter sIf(boolean flag) { 300 if (flag && ! useWhitespace) 301 w(' '); 302 return this; 303 } 304 305 /** 306 * Writes a newline to the writer if the {@code useWhitespace} setting is enabled and the boolean flag is true. 307 * 308 * @param flag The boolean flag. 309 * @param indent The current indentation level. 310 * @return This object. 311 */ 312 public SerializerWriter nlIf(boolean flag, int indent) { 313 if (flag && useWhitespace && indent <= maxIndent) 314 w('\n'); 315 return this; 316 } 317 318 /** 319 * Writes the specified text to the writer if it isn't <jk>null</jk>. 320 * 321 * @param value The text to write. 322 * @return This object. 323 */ 324 public SerializerWriter append(Object value) { 325 w(value == null ? null : value.toString()); 326 return this; 327 } 328 329 /** 330 * Writes the specified text to the writer if it isn't <jk>null</jk>. 331 * 332 * @param value The text to write. 333 * @return This object. 334 */ 335 public SerializerWriter append(String value) { 336 if (value != null) 337 w(value); 338 return this; 339 } 340 341 /** 342 * Writes the specified text to the writer if b is true. 343 * 344 * @param flag Boolean flag. 345 * @param value The text to write. 346 * @return This object. 347 */ 348 public SerializerWriter appendIf(boolean flag, String value) { 349 if (flag) 350 w(value); 351 return this; 352 } 353 354 /** 355 * Writes the specified text to the writer if b is true. 356 * 357 * @param flag Boolean flag. 358 * @param value The text to write. 359 * @return This object. 360 */ 361 public SerializerWriter appendIf(boolean flag, char value) { 362 if (flag) 363 w(value); 364 return this; 365 } 366 367 /** 368 * Writes the specified character to the writer. 369 * 370 * @param value The character to write. 371 * @return This object. 372 */ 373 public SerializerWriter w(char value) { 374 try { 375 out.write(value); 376 } catch (IOException e) { 377 throw new SerializeException(e); 378 } 379 return this; 380 } 381 382 /** 383 * Writes the specified string to the writer. 384 * 385 * @param value The string to write. 386 * @return This object. 387 */ 388 public SerializerWriter w(String value) { 389 try { 390 out.write(value); 391 } catch (IOException e) { 392 throw new SerializeException(e); 393 } 394 return this; 395 } 396 397 //----------------------------------------------------------------------------------------------------------------- 398 // Overridden methods 399 //----------------------------------------------------------------------------------------------------------------- 400 401 @Override /* Writer */ 402 public SerializerWriter append(char c) { 403 try { 404 out.write(c); 405 } catch (IOException e) { 406 throw new SerializeException(e); 407 } 408 return this; 409 } 410 411 @Override /* Writer */ 412 public void write(char[] cbuf, int off, int len) { 413 try { 414 out.write(cbuf, off, len); 415 } catch (IOException e) { 416 throw new SerializeException(e); 417 } 418 } 419 420 @Override /* Writer */ 421 public void flush() throws IOException { 422 out.flush(); 423 } 424 425 @Override /* Writer */ 426 public void close() throws IOException { 427 out.close(); 428 } 429}