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 * Performs a carriage return. 080 * 081 * <p> 082 * Adds a newline and the specified number of tabs (if the {@code useWhitespace} setting is enabled) to the output. 083 * 084 * @param depth The indentation. 085 * @return This object. 086 */ 087 @FluentSetter 088 public SerializerWriter cr(int depth) { 089 if (useWhitespace && depth <= maxIndent) 090 return nl(depth).i(depth); 091 return this; 092 } 093 094 /** 095 * Performs a carriage return at the end of a line. 096 * 097 * <p> 098 * Adds a newline and the specified number of tabs (if the {@code useWhitespace} setting is enabled) to the output. 099 * 100 * @param depth The indentation. 101 * @return This object. 102 */ 103 @FluentSetter 104 public SerializerWriter cre(int depth) { 105 if (useWhitespace && depth <= maxIndent-1) 106 return nl(depth).i(depth); 107 return this; 108 } 109 110 /** 111 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text, followed by a newline 112 * (if the {@code useWhitespace} setting is enabled). 113 * 114 * @param indent The number of tabs to indent. 115 * @param value The text to write. 116 * @return This object. 117 */ 118 @FluentSetter 119 public SerializerWriter appendln(int indent, String value) { 120 return append(indent, true, value); 121 } 122 123 /** 124 * Writes the specified text followed by a newline (if the {@code useWhitespace} setting is enabled). 125 * 126 * @param value The text to write. 127 * @return This object. 128 */ 129 @FluentSetter 130 public SerializerWriter appendln(String value) { 131 return append(0, true, value); 132 } 133 134 /** 135 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text. 136 * 137 * @param indent The number of tabs to indent. 138 * @param value The text to write. 139 * @return This object. 140 */ 141 @FluentSetter 142 public SerializerWriter append(int indent, String value) { 143 return append(indent, false, value); 144 } 145 146 /** 147 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text. 148 * 149 * @param indent The number of tabs to indent. 150 * @param value The character to write. 151 * @return This object. 152 */ 153 @FluentSetter 154 public SerializerWriter append(int indent, char value) { 155 return i(indent).w(value); 156 } 157 158 /** 159 * Writes an indent (if the {@code useWhitespace} setting is enabled), followed by text, optionally followed by a 160 * newline (if the {@code useWhitespace} setting is enabled). 161 * 162 * @param indent The number of tabs to indent. 163 * @param newline If <jk>true</jk>, then a newline is written. 164 * @param value The text to write. 165 * @throws IOException If a problem occurred trying to write to the writer. 166 * @return This object. 167 */ 168 private SerializerWriter append(int indent, boolean newline, String value) { 169 170 if (value == null) 171 return this; 172 173 // If text contains newlines, we break it up into lines and indent them separately. 174 if (value.indexOf('\n') != -1 && useWhitespace && indent <= maxIndent) { 175 for (StringTokenizer st = new StringTokenizer(value, "\n"); st.hasMoreTokens();) 176 i(indent).w(st.nextToken()).w("\n"); 177 } else { 178 i(indent).w(value); 179 } 180 181 if (newline) 182 nl(indent); 183 184 return this; 185 } 186 187 /** 188 * Appends the specified object as a URI. 189 * 190 * <p> 191 * Object is converted to a <c>String</c> using <c>toString()</c>, so this will work on {@link URL} or 192 * {@link URI} objects, or any other type that returns a URI via it's <c>toString()</c> method. 193 * 194 * <p> 195 * The URI is resolved based on the {@link Serializer.Builder#uriRelativity(UriRelativity)} and 196 * {@link Serializer.Builder#uriResolution(UriResolution)} settings and the {@link UriContext} that's part of the 197 * session. 198 * 199 * @param value The URI to serialize. 200 * @return This object. 201 */ 202 @FluentSetter 203 public SerializerWriter appendUri(Object value) { 204 uriResolver.append(this, value); 205 return this; 206 } 207 208 /** 209 * Appends the specified characters to this writer. 210 * 211 * @param value The characters to append to this writer. 212 * @return This object. 213 */ 214 @FluentSetter 215 public SerializerWriter append(char[] value) { 216 for (char c : value) 217 w(c); 218 return this; 219 } 220 221 /** 222 * Adds a whitespace character to the output if the {@code useWhitespace} setting is enabled. 223 * 224 * @return This object. 225 */ 226 @FluentSetter 227 public SerializerWriter s() { 228 if (useWhitespace) 229 w(' '); 230 return this; 231 } 232 233 /** 234 * Adds the quote character specified by the {@code quoteChar} setting to the output. 235 * 236 * @return This object. 237 */ 238 @FluentSetter 239 public SerializerWriter q() { 240 w(quoteChar); 241 return this; 242 } 243 244 /** 245 * Writes an indent to the writer if the {@code useWhitespace} setting is enabled. 246 * 247 * @param indent The number of tabs to indent. 248 * @return This object. 249 */ 250 @FluentSetter 251 public SerializerWriter i(int indent) { 252 if (useWhitespace && indent <= maxIndent) 253 for (int i = 0; i < indent; i++) 254 w('\t'); 255 return this; 256 } 257 258 /** 259 * Writes an end-of-line 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 ie(int indent) { 266 if (useWhitespace && indent <= maxIndent-1) 267 for (int i = 0; i < indent; i++) 268 w('\t'); 269 return this; 270 } 271 272 /** 273 * Writes a newline to the writer if the {@code useWhitespace} setting is enabled. 274 * 275 * @param indent The current indentation level. 276 * @return This object. 277 */ 278 @FluentSetter 279 public SerializerWriter nl(int indent) { 280 if (useWhitespace && indent <= maxIndent) 281 w('\n'); 282 return this; 283 } 284 285 /** 286 * Writes a space if the boolean expression is <jk>true</jk> and {@code useWhitespace} is false. 287 * 288 * <p> 289 * Intended for cases in XML where text should be separated by either a space or newline. 290 * This ensures the text is separated by a space if whitespace is disabled. 291 * 292 * @param flag The boolean flag. 293 * @return This object. 294 */ 295 @FluentSetter 296 public SerializerWriter sIf(boolean flag) { 297 if (flag && ! useWhitespace) 298 w(' '); 299 return this; 300 } 301 302 /** 303 * Writes a newline to the writer if the {@code useWhitespace} setting is enabled and the boolean flag is true. 304 * 305 * @param flag The boolean flag. 306 * @param indent The current indentation level. 307 * @return This object. 308 */ 309 @FluentSetter 310 public SerializerWriter nlIf(boolean flag, int indent) { 311 if (flag && useWhitespace && indent <= maxIndent) 312 w('\n'); 313 return this; 314 } 315 316 /** 317 * Writes the specified text to the writer if it isn't <jk>null</jk>. 318 * 319 * @param value The text to write. 320 * @return This object. 321 */ 322 @FluentSetter 323 public SerializerWriter append(Object value) { 324 w(value == null ? null : value.toString()); 325 return this; 326 } 327 328 /** 329 * Writes the specified text to the writer if it isn't <jk>null</jk>. 330 * 331 * @param value The text to write. 332 * @return This object. 333 */ 334 @FluentSetter 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 @FluentSetter 349 public SerializerWriter appendIf(boolean flag, String value) { 350 if (flag) 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, char value) { 364 if (flag) 365 w(value); 366 return this; 367 } 368 369 /** 370 * Writes the specified character to the writer. 371 * 372 * @param value The character to write. 373 * @return This object. 374 */ 375 @FluentSetter 376 public SerializerWriter w(char value) { 377 try { 378 out.write(value); 379 } catch (IOException e) { 380 throw new SerializeException(e); 381 } 382 return this; 383 } 384 385 /** 386 * Writes the specified string to the writer. 387 * 388 * @param value The string to write. 389 * @return This object. 390 */ 391 @FluentSetter 392 public SerializerWriter w(String value) { 393 try { 394 out.write(value); 395 } catch (IOException e) { 396 throw new SerializeException(e); 397 } 398 return this; 399 } 400 401 //----------------------------------------------------------------------------------------------------------------- 402 // Overridden methods 403 //----------------------------------------------------------------------------------------------------------------- 404 405 @Override /* Writer */ 406 public SerializerWriter append(char c) { 407 try { 408 out.write(c); 409 } catch (IOException e) { 410 throw new SerializeException(e); 411 } 412 return this; 413 } 414 415 @Override /* Writer */ 416 public void write(char[] cbuf, int off, int len) { 417 try { 418 out.write(cbuf, off, len); 419 } catch (IOException e) { 420 throw new SerializeException(e); 421 } 422 } 423 424 @Override /* Writer */ 425 public void flush() throws IOException { 426 out.flush(); 427 } 428 429 @Override /* Writer */ 430 public void close() throws IOException { 431 out.close(); 432 } 433 434 // <FluentSetters> 435 436 // </FluentSetters> 437}