1 package com.fasterxml.jackson.core.util; 2 3 import java.io.*; 4 5 import com.fasterxml.jackson.core.*; 6 import com.fasterxml.jackson.core.io.SerializedString; 7 8 /** 9 * Default {@link PrettyPrinter} implementation that uses 2-space 10 * indentation with platform-default linefeeds. 11 * Usually this class is not instantiated directly, but instead 12 * method {@link JsonGenerator#useDefaultPrettyPrinter} is 13 * used, which will use an instance of this class for operation. 14 */ 15 @SuppressWarnings("serial") 16 public class DefaultPrettyPrinter 17 implements PrettyPrinter, Instantiatable<DefaultPrettyPrinter>, 18 java.io.Serializable 19 { 20 private static final long serialVersionUID = 1; 21 22 /** 23 * Constant that specifies default "root-level" separator to use between 24 * root values: a single space character. 25 * 26 * @since 2.1 27 */ 28 public final static SerializedString DEFAULT_ROOT_VALUE_SEPARATOR = new SerializedString(" "); 29 30 /** 31 * Interface that defines objects that can produce indentation used 32 * to separate object entries and array values. Indentation in this 33 * context just means insertion of white space, independent of whether 34 * linefeeds are output. 35 */ 36 public interface Indenter 37 { writeIndentation(JsonGenerator g, int level)38 void writeIndentation(JsonGenerator g, int level) throws IOException; 39 40 /** 41 * @return True if indenter is considered inline (does not add linefeeds), 42 * false otherwise 43 */ isInline()44 boolean isInline(); 45 } 46 47 // // // Config, indentation 48 49 /** 50 * By default, let's use only spaces to separate array values. 51 */ 52 protected Indenter _arrayIndenter = FixedSpaceIndenter.instance; 53 54 /** 55 * By default, let's use linefeed-adding indenter for separate 56 * object entries. We'll further configure indenter to use 57 * system-specific linefeeds, and 2 spaces per level (as opposed to, 58 * say, single tabs) 59 */ 60 protected Indenter _objectIndenter = DefaultIndenter.SYSTEM_LINEFEED_INSTANCE; 61 62 /** 63 * String printed between root-level values, if any. 64 */ 65 protected final SerializableString _rootSeparator; 66 67 // // // Config, other white space configuration 68 69 /** 70 * By default we will add spaces around colons used to 71 * separate object fields and values. 72 * If disabled, will not use spaces around colon. 73 */ 74 protected boolean _spacesInObjectEntries = true; 75 76 // // // State: 77 78 /** 79 * Number of open levels of nesting. Used to determine amount of 80 * indentation to use. 81 */ 82 protected transient int _nesting; 83 84 /** 85 * @since 2.9 86 */ 87 protected Separators _separators; 88 89 /** 90 * @since 2.9 91 */ 92 protected String _objectFieldValueSeparatorWithSpaces; 93 94 /* 95 /********************************************************** 96 /* Life-cycle (construct, configure) 97 /********************************************************** 98 */ 99 DefaultPrettyPrinter()100 public DefaultPrettyPrinter() { 101 this(DEFAULT_ROOT_VALUE_SEPARATOR); 102 } 103 104 /** 105 * Constructor that specifies separator String to use between root values; 106 * if null, no separator is printed. 107 *<p> 108 * Note: simply constructs a {@link SerializedString} out of parameter, 109 * calls {@link #DefaultPrettyPrinter(SerializableString)} 110 * 111 * @param rootSeparator 112 * 113 * @since 2.1 114 */ DefaultPrettyPrinter(String rootSeparator)115 public DefaultPrettyPrinter(String rootSeparator) { 116 this((rootSeparator == null) ? null : new SerializedString(rootSeparator)); 117 } 118 119 /** 120 * Constructor that specifies separator String to use between root values; 121 * if null, no separator is printed. 122 * 123 * @param rootSeparator 124 * 125 * @since 2.1 126 */ DefaultPrettyPrinter(SerializableString rootSeparator)127 public DefaultPrettyPrinter(SerializableString rootSeparator) { 128 _rootSeparator = rootSeparator; 129 withSeparators(DEFAULT_SEPARATORS); 130 } 131 DefaultPrettyPrinter(DefaultPrettyPrinter base)132 public DefaultPrettyPrinter(DefaultPrettyPrinter base) { 133 this(base, base._rootSeparator); 134 } 135 DefaultPrettyPrinter(DefaultPrettyPrinter base, SerializableString rootSeparator)136 public DefaultPrettyPrinter(DefaultPrettyPrinter base, 137 SerializableString rootSeparator) 138 { 139 _arrayIndenter = base._arrayIndenter; 140 _objectIndenter = base._objectIndenter; 141 _spacesInObjectEntries = base._spacesInObjectEntries; 142 _nesting = base._nesting; 143 144 _separators = base._separators; 145 _objectFieldValueSeparatorWithSpaces = base._objectFieldValueSeparatorWithSpaces; 146 147 _rootSeparator = rootSeparator; 148 } 149 withRootSeparator(SerializableString rootSeparator)150 public DefaultPrettyPrinter withRootSeparator(SerializableString rootSeparator) 151 { 152 if (_rootSeparator == rootSeparator || 153 (rootSeparator != null && rootSeparator.equals(_rootSeparator))) { 154 return this; 155 } 156 return new DefaultPrettyPrinter(this, rootSeparator); 157 } 158 159 /** 160 * @since 2.6 161 */ withRootSeparator(String rootSeparator)162 public DefaultPrettyPrinter withRootSeparator(String rootSeparator) { 163 return withRootSeparator((rootSeparator == null) ? null : new SerializedString(rootSeparator)); 164 } 165 indentArraysWith(Indenter i)166 public void indentArraysWith(Indenter i) { 167 _arrayIndenter = (i == null) ? NopIndenter.instance : i; 168 } 169 indentObjectsWith(Indenter i)170 public void indentObjectsWith(Indenter i) { 171 _objectIndenter = (i == null) ? NopIndenter.instance : i; 172 } 173 174 /** 175 * @since 2.3 176 */ withArrayIndenter(Indenter i)177 public DefaultPrettyPrinter withArrayIndenter(Indenter i) { 178 if (i == null) { 179 i = NopIndenter.instance; 180 } 181 if (_arrayIndenter == i) { 182 return this; 183 } 184 DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this); 185 pp._arrayIndenter = i; 186 return pp; 187 } 188 189 /** 190 * @since 2.3 191 */ withObjectIndenter(Indenter i)192 public DefaultPrettyPrinter withObjectIndenter(Indenter i) { 193 if (i == null) { 194 i = NopIndenter.instance; 195 } 196 if (_objectIndenter == i) { 197 return this; 198 } 199 DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this); 200 pp._objectIndenter = i; 201 return pp; 202 } 203 204 /** 205 * "Mutant factory" method that will return a pretty printer instance 206 * that does use spaces inside object entries; if 'this' instance already 207 * does this, it is returned; if not, a new instance will be constructed 208 * and returned. 209 * 210 * @since 2.3 211 */ withSpacesInObjectEntries()212 public DefaultPrettyPrinter withSpacesInObjectEntries() { 213 return _withSpaces(true); 214 } 215 216 /** 217 * "Mutant factory" method that will return a pretty printer instance 218 * that does not use spaces inside object entries; if 'this' instance already 219 * does this, it is returned; if not, a new instance will be constructed 220 * and returned. 221 * 222 * @since 2.3 223 */ withoutSpacesInObjectEntries()224 public DefaultPrettyPrinter withoutSpacesInObjectEntries() { 225 return _withSpaces(false); 226 } 227 _withSpaces(boolean state)228 protected DefaultPrettyPrinter _withSpaces(boolean state) 229 { 230 if (_spacesInObjectEntries == state) { 231 return this; 232 } 233 DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this); 234 pp._spacesInObjectEntries = state; 235 return pp; 236 } 237 238 /** 239 * @since 2.9 240 */ withSeparators(Separators separators)241 public DefaultPrettyPrinter withSeparators(Separators separators) { 242 _separators = separators; 243 _objectFieldValueSeparatorWithSpaces = " " + separators.getObjectFieldValueSeparator() + " "; 244 return this; 245 } 246 247 /* 248 /********************************************************** 249 /* Instantiatable impl 250 /********************************************************** 251 */ 252 253 @Override createInstance()254 public DefaultPrettyPrinter createInstance() { 255 if (getClass() != DefaultPrettyPrinter.class) { // since 2.10 256 throw new IllegalStateException("Failed `createInstance()`: "+getClass().getName() 257 +" does not override method; it has to"); 258 } 259 return new DefaultPrettyPrinter(this); 260 } 261 262 /* 263 /********************************************************** 264 /* PrettyPrinter impl 265 /********************************************************** 266 */ 267 268 @Override writeRootValueSeparator(JsonGenerator g)269 public void writeRootValueSeparator(JsonGenerator g) throws IOException 270 { 271 if (_rootSeparator != null) { 272 g.writeRaw(_rootSeparator); 273 } 274 } 275 276 @Override writeStartObject(JsonGenerator g)277 public void writeStartObject(JsonGenerator g) throws IOException 278 { 279 g.writeRaw('{'); 280 if (!_objectIndenter.isInline()) { 281 ++_nesting; 282 } 283 } 284 285 @Override beforeObjectEntries(JsonGenerator g)286 public void beforeObjectEntries(JsonGenerator g) throws IOException 287 { 288 _objectIndenter.writeIndentation(g, _nesting); 289 } 290 291 /** 292 * Method called after an object field has been output, but 293 * before the value is output. 294 *<p> 295 * Default handling (without pretty-printing) will output a single 296 * colon to separate the two. Pretty-printer is 297 * to output a colon as well, but can surround that with other 298 * (white-space) decoration. 299 */ 300 @Override writeObjectFieldValueSeparator(JsonGenerator g)301 public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException 302 { 303 if (_spacesInObjectEntries) { 304 g.writeRaw(_objectFieldValueSeparatorWithSpaces); 305 } else { 306 g.writeRaw(_separators.getObjectFieldValueSeparator()); 307 } 308 } 309 310 /** 311 * Method called after an object entry (field:value) has been completely 312 * output, and before another value is to be output. 313 *<p> 314 * Default handling (without pretty-printing) will output a single 315 * comma to separate the two. Pretty-printer is 316 * to output a comma as well, but can surround that with other 317 * (white-space) decoration. 318 */ 319 @Override writeObjectEntrySeparator(JsonGenerator g)320 public void writeObjectEntrySeparator(JsonGenerator g) throws IOException 321 { 322 g.writeRaw(_separators.getObjectEntrySeparator()); 323 _objectIndenter.writeIndentation(g, _nesting); 324 } 325 326 @Override writeEndObject(JsonGenerator g, int nrOfEntries)327 public void writeEndObject(JsonGenerator g, int nrOfEntries) throws IOException 328 { 329 if (!_objectIndenter.isInline()) { 330 --_nesting; 331 } 332 if (nrOfEntries > 0) { 333 _objectIndenter.writeIndentation(g, _nesting); 334 } else { 335 g.writeRaw(' '); 336 } 337 g.writeRaw('}'); 338 } 339 340 @Override writeStartArray(JsonGenerator g)341 public void writeStartArray(JsonGenerator g) throws IOException 342 { 343 if (!_arrayIndenter.isInline()) { 344 ++_nesting; 345 } 346 g.writeRaw('['); 347 } 348 349 @Override beforeArrayValues(JsonGenerator g)350 public void beforeArrayValues(JsonGenerator g) throws IOException { 351 _arrayIndenter.writeIndentation(g, _nesting); 352 } 353 354 /** 355 * Method called after an array value has been completely 356 * output, and before another value is to be output. 357 *<p> 358 * Default handling (without pretty-printing) will output a single 359 * comma to separate the two. Pretty-printer is 360 * to output a comma as well, but can surround that with other 361 * (white-space) decoration. 362 */ 363 @Override writeArrayValueSeparator(JsonGenerator g)364 public void writeArrayValueSeparator(JsonGenerator g) throws IOException 365 { 366 g.writeRaw(_separators.getArrayValueSeparator()); 367 _arrayIndenter.writeIndentation(g, _nesting); 368 } 369 370 @Override writeEndArray(JsonGenerator g, int nrOfValues)371 public void writeEndArray(JsonGenerator g, int nrOfValues) throws IOException 372 { 373 if (!_arrayIndenter.isInline()) { 374 --_nesting; 375 } 376 if (nrOfValues > 0) { 377 _arrayIndenter.writeIndentation(g, _nesting); 378 } else { 379 g.writeRaw(' '); 380 } 381 g.writeRaw(']'); 382 } 383 384 /* 385 /********************************************************** 386 /* Helper classes 387 /********************************************************** 388 */ 389 390 /** 391 * Dummy implementation that adds no indentation whatsoever 392 */ 393 public static class NopIndenter 394 implements Indenter, java.io.Serializable 395 { 396 public static final NopIndenter instance = new NopIndenter(); 397 398 @Override writeIndentation(JsonGenerator g, int level)399 public void writeIndentation(JsonGenerator g, int level) throws IOException { } 400 401 @Override isInline()402 public boolean isInline() { return true; } 403 } 404 405 /** 406 * This is a very simple indenter that only adds a 407 * single space for indentation. It is used as the default 408 * indenter for array values. 409 */ 410 public static class FixedSpaceIndenter extends NopIndenter 411 { 412 public static final FixedSpaceIndenter instance = new FixedSpaceIndenter(); 413 414 @Override writeIndentation(JsonGenerator g, int level)415 public void writeIndentation(JsonGenerator g, int level) throws IOException 416 { 417 g.writeRaw(' '); 418 } 419 420 @Override isInline()421 public boolean isInline() { return true; } 422 } 423 } 424