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