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 {
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 */
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
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 */
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 */
127 public DefaultPrettyPrinter(SerializableString rootSeparator) {
128 _rootSeparator = rootSeparator;
129 withSeparators(DEFAULT_SEPARATORS);
130 }
131
132 public DefaultPrettyPrinter(DefaultPrettyPrinter base) {
133 this(base, base._rootSeparator);
134 }
135
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
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 */
162 public DefaultPrettyPrinter withRootSeparator(String rootSeparator) {
163 return withRootSeparator((rootSeparator == null) ? null : new SerializedString(rootSeparator));
164 }
165
166 public void indentArraysWith(Indenter i) {
167 _arrayIndenter = (i == null) ? NopIndenter.instance : i;
168 }
169
170 public void indentObjectsWith(Indenter i) {
171 _objectIndenter = (i == null) ? NopIndenter.instance : i;
172 }
173
174 /**
175 * @since 2.3
176 */
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 */
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 */
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 */
224 public DefaultPrettyPrinter withoutSpacesInObjectEntries() {
225 return _withSpaces(false);
226 }
227
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 */
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
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
269 public void writeRootValueSeparator(JsonGenerator g) throws IOException
270 {
271 if (_rootSeparator != null) {
272 g.writeRaw(_rootSeparator);
273 }
274 }
275
276 @Override
277 public void writeStartObject(JsonGenerator g) throws IOException
278 {
279 g.writeRaw('{');
280 if (!_objectIndenter.isInline()) {
281 ++_nesting;
282 }
283 }
284
285 @Override
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
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
320 public void writeObjectEntrySeparator(JsonGenerator g) throws IOException
321 {
322 g.writeRaw(_separators.getObjectEntrySeparator());
323 _objectIndenter.writeIndentation(g, _nesting);
324 }
325
326 @Override
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
341 public void writeStartArray(JsonGenerator g) throws IOException
342 {
343 if (!_arrayIndenter.isInline()) {
344 ++_nesting;
345 }
346 g.writeRaw('[');
347 }
348
349 @Override
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
364 public void writeArrayValueSeparator(JsonGenerator g) throws IOException
365 {
366 g.writeRaw(_separators.getArrayValueSeparator());
367 _arrayIndenter.writeIndentation(g, _nesting);
368 }
369
370 @Override
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
399 public void writeIndentation(JsonGenerator g, int level) throws IOException { }
400
401 @Override
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
415 public void writeIndentation(JsonGenerator g, int level) throws IOException
416 {
417 g.writeRaw(' ');
418 }
419
420 @Override
421 public boolean isInline() { return true; }
422 }
423 }
424