1 package com.fasterxml.jackson.core.base;
2
3 import com.fasterxml.jackson.core.*;
4 import com.fasterxml.jackson.core.json.DupDetector;
5 import com.fasterxml.jackson.core.json.JsonWriteContext;
6 import com.fasterxml.jackson.core.json.PackageVersion;
7 import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
8
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.math.BigDecimal;
12
13 /**
14 * This base class implements part of API that a JSON generator exposes
15 * to applications, adds shared internal methods that sub-classes
16 * can use and adds some abstract methods sub-classes must implement.
17 */
18 public abstract class GeneratorBase extends JsonGenerator
19 {
20 public final static int SURR1_FIRST = 0xD800;
21 public final static int SURR1_LAST = 0xDBFF;
22 public final static int SURR2_FIRST = 0xDC00;
23 public final static int SURR2_LAST = 0xDFFF;
24
25 /**
26 * Set of feature masks related to features that need updates of other
27 * local configuration or state.
28 *
29 * @since 2.5
30 */
31 @SuppressWarnings("deprecation")
32 protected final static int DERIVED_FEATURES_MASK =
33 Feature.WRITE_NUMBERS_AS_STRINGS.getMask()
34 | Feature.ESCAPE_NON_ASCII.getMask()
35 | Feature.STRICT_DUPLICATE_DETECTION.getMask()
36 ;
37
38 // // // Constants for validation messages (since 2.6)
39
40 protected final static String WRITE_BINARY = "write a binary value";
41 protected final static String WRITE_BOOLEAN = "write a boolean value";
42 protected final static String WRITE_NULL = "write a null";
43 protected final static String WRITE_NUMBER = "write a number";
44 protected final static String WRITE_RAW = "write a raw (unencoded) value";
45 protected final static String WRITE_STRING = "write a string";
46
47 /**
48 * This value is the limit of scale allowed for serializing {@link BigDecimal}
49 * in "plain" (non-engineering) notation; intent is to prevent asymmetric
50 * attack whereupon simple eng-notation with big scale is used to generate
51 * huge "plain" serialization. See [core#315] for details.
52 *
53 * @since 2.7.7
54 */
55 protected final static int MAX_BIG_DECIMAL_SCALE = 9999;
56
57 /*
58 /**********************************************************
59 /* Configuration
60 /**********************************************************
61 */
62
63 protected ObjectCodec _objectCodec;
64
65 /**
66 * Bit flag composed of bits that indicate which
67 * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
68 * are enabled.
69 */
70 protected int _features;
71
72 /**
73 * Flag set to indicate that implicit conversion from number
74 * to JSON String is needed (as per
75 * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_NUMBERS_AS_STRINGS}).
76 */
77 protected boolean _cfgNumbersAsStrings;
78
79 /*
80 /**********************************************************
81 /* State
82 /**********************************************************
83 */
84
85 /**
86 * Object that keeps track of the current contextual state
87 * of the generator.
88 */
89 protected JsonWriteContext _writeContext;
90
91 /**
92 * Flag that indicates whether generator is closed or not. Gets
93 * set when it is closed by an explicit call
94 * ({@link #close}).
95 */
96 protected boolean _closed;
97
98 /*
99 /**********************************************************
100 /* Life-cycle
101 /**********************************************************
102 */
103
104 @SuppressWarnings("deprecation")
105 protected GeneratorBase(int features, ObjectCodec codec) {
106 super();
107 _features = features;
108 _objectCodec = codec;
109 DupDetector dups = Feature.STRICT_DUPLICATE_DETECTION.enabledIn(features)
110 ? DupDetector.rootDetector(this) : null;
111 _writeContext = JsonWriteContext.createRootContext(dups);
112 _cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(features);
113 }
114
115 /**
116 * @since 2.5
117 */
118 @SuppressWarnings("deprecation")
119 protected GeneratorBase(int features, ObjectCodec codec, JsonWriteContext ctxt) {
120 super();
121 _features = features;
122 _objectCodec = codec;
123 _writeContext = ctxt;
124 _cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(features);
125 }
126
127 /**
128 * Implemented with standard version number detection algorithm, typically using
129 * a simple generated class, with information extracted from Maven project file
130 * during build.
131 */
132 @Override public Version version() { return PackageVersion.VERSION; }
133
134 @Override
135 public Object getCurrentValue() {
136 return _writeContext.getCurrentValue();
137 }
138
139 @Override
140 public void setCurrentValue(Object v) {
141 if (_writeContext != null) {
142 _writeContext.setCurrentValue(v);
143 }
144 }
145
146 /*
147 /**********************************************************
148 /* Configuration
149 /**********************************************************
150 */
151
152
153 @Override public final boolean isEnabled(Feature f) { return (_features & f.getMask()) != 0; }
154 @Override public int getFeatureMask() { return _features; }
155
156 //public JsonGenerator configure(Feature f, boolean state) { }
157
158 @SuppressWarnings("deprecation")
159 @Override
160 public JsonGenerator enable(Feature f) {
161 final int mask = f.getMask();
162 _features |= mask;
163 if ((mask & DERIVED_FEATURES_MASK) != 0) {
164 // why not switch? Requires addition of a generated class, alas
165 if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
166 _cfgNumbersAsStrings = true;
167 } else if (f == Feature.ESCAPE_NON_ASCII) {
168 setHighestNonEscapedChar(127);
169 } else if (f == Feature.STRICT_DUPLICATE_DETECTION) {
170 if (_writeContext.getDupDetector() == null) { // but only if disabled currently
171 _writeContext = _writeContext.withDupDetector(DupDetector.rootDetector(this));
172 }
173 }
174 }
175 return this;
176 }
177
178 @SuppressWarnings("deprecation")
179 @Override
180 public JsonGenerator disable(Feature f) {
181 final int mask = f.getMask();
182 _features &= ~mask;
183 if ((mask & DERIVED_FEATURES_MASK) != 0) {
184 if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
185 _cfgNumbersAsStrings = false;
186 } else if (f == Feature.ESCAPE_NON_ASCII) {
187 setHighestNonEscapedChar(0);
188 } else if (f == Feature.STRICT_DUPLICATE_DETECTION) {
189 _writeContext = _writeContext.withDupDetector(null);
190 }
191 }
192 return this;
193 }
194
195 @Override
196 @Deprecated
197 public JsonGenerator setFeatureMask(int newMask) {
198 int changed = newMask ^ _features;
199 _features = newMask;
200 if (changed != 0) {
201 _checkStdFeatureChanges(newMask, changed);
202 }
203 return this;
204 }
205
206 @Override // since 2.7
207 public JsonGenerator overrideStdFeatures(int values, int mask) {
208 int oldState = _features;
209 int newState = (oldState & ~mask) | (values & mask);
210 int changed = oldState ^ newState;
211 if (changed != 0) {
212 _features = newState;
213 _checkStdFeatureChanges(newState, changed);
214 }
215 return this;
216 }
217
218 /**
219 * Helper method called to verify changes to standard features.
220 *
221 * @param newFeatureFlags Bitflag of standard features after they were changed
222 * @param changedFeatures Bitflag of standard features for which setting
223 * did change
224 *
225 * @since 2.7
226 */
227 @SuppressWarnings("deprecation")
228 protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures)
229 {
230 if ((changedFeatures & DERIVED_FEATURES_MASK) == 0) {
231 return;
232 }
233 _cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(newFeatureFlags);
234 if (Feature.ESCAPE_NON_ASCII.enabledIn(changedFeatures)) {
235 if (Feature.ESCAPE_NON_ASCII.enabledIn(newFeatureFlags)) {
236 setHighestNonEscapedChar(127);
237 } else {
238 setHighestNonEscapedChar(0);
239 }
240 }
241 if (Feature.STRICT_DUPLICATE_DETECTION.enabledIn(changedFeatures)) {
242 if (Feature.STRICT_DUPLICATE_DETECTION.enabledIn(newFeatureFlags)) { // enabling
243 if (_writeContext.getDupDetector() == null) { // but only if disabled currently
244 _writeContext = _writeContext.withDupDetector(DupDetector.rootDetector(this));
245 }
246 } else { // disabling
247 _writeContext = _writeContext.withDupDetector(null);
248 }
249 }
250 }
251
252 @Override public JsonGenerator useDefaultPrettyPrinter() {
253 // Should not override a pretty printer if one already assigned.
254 if (getPrettyPrinter() != null) {
255 return this;
256 }
257 return setPrettyPrinter(_constructDefaultPrettyPrinter());
258 }
259
260 @Override public JsonGenerator setCodec(ObjectCodec oc) {
261 _objectCodec = oc;
262 return this;
263 }
264
265 @Override public ObjectCodec getCodec() { return _objectCodec; }
266
267 /*
268 /**********************************************************
269 /* Public API, accessors
270 /**********************************************************
271 */
272
273 /**
274 * Note: type was co-variant until Jackson 2.7; reverted back to
275 * base type in 2.8 to allow for overriding by subtypes that use
276 * custom context type.
277 */
278 @Override public JsonStreamContext getOutputContext() { return _writeContext; }
279
280 /*
281 /**********************************************************
282 /* Public API, write methods, structural
283 /**********************************************************
284 */
285
286 //public void writeStartArray() throws IOException
287 //public void writeEndArray() throws IOException
288 //public void writeStartObject() throws IOException
289 //public void writeEndObject() throws IOException
290
291 @Override // since 2.8
292 public void writeStartObject(Object forValue) throws IOException
293 {
294 writeStartObject();
295 if (forValue != null) {
296 setCurrentValue(forValue);
297 }
298 }
299
300 /*
301 /**********************************************************
302 /* Public API, write methods, textual
303 /**********************************************************
304 */
305
306 @Override public void writeFieldName(SerializableString name) throws IOException {
307 writeFieldName(name.getValue());
308 }
309
310 //public abstract void writeString(String text) throws IOException;
311
312 //public abstract void writeString(char[] text, int offset, int len) throws IOException;
313
314 //public abstract void writeString(Reader reader, int len) throws IOException;
315
316 //public abstract void writeRaw(String text) throws IOException,;
317
318 //public abstract void writeRaw(char[] text, int offset, int len) throws IOException;
319
320 @Override
321 public void writeString(SerializableString text) throws IOException {
322 writeString(text.getValue());
323 }
324
325 @Override public void writeRawValue(String text) throws IOException {
326 _verifyValueWrite("write raw value");
327 writeRaw(text);
328 }
329
330 @Override public void writeRawValue(String text, int offset, int len) throws IOException {
331 _verifyValueWrite("write raw value");
332 writeRaw(text, offset, len);
333 }
334
335 @Override public void writeRawValue(char[] text, int offset, int len) throws IOException {
336 _verifyValueWrite("write raw value");
337 writeRaw(text, offset, len);
338 }
339
340 @Override public void writeRawValue(SerializableString text) throws IOException {
341 _verifyValueWrite("write raw value");
342 writeRaw(text);
343 }
344
345 @Override
346 public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException {
347 // Let's implement this as "unsupported" to make it easier to add new parser impls
348 _reportUnsupportedOperation();
349 return 0;
350 }
351
352 /*
353 /**********************************************************
354 /* Public API, write methods, primitive
355 /**********************************************************
356 */
357
358 // Not implemented at this level, added as placeholders
359
360 /*
361 public abstract void writeNumber(int i)
362 public abstract void writeNumber(long l)
363 public abstract void writeNumber(double d)
364 public abstract void writeNumber(float f)
365 public abstract void writeNumber(BigDecimal dec)
366 public abstract void writeBoolean(boolean state)
367 public abstract void writeNull()
368 */
369
370 /*
371 /**********************************************************
372 /* Public API, write methods, POJOs, trees
373 /**********************************************************
374 */
375
376 @Override
377 public void writeObject(Object value) throws IOException {
378 if (value == null) {
379 // important: call method that does check value write:
380 writeNull();
381 } else {
382 /* 02-Mar-2009, tatu: we are NOT to call _verifyValueWrite here,
383 * because that will be done when codec actually serializes
384 * contained POJO. If we did call it it would advance state
385 * causing exception later on
386 */
387 if (_objectCodec != null) {
388 _objectCodec.writeValue(this, value);
389 return;
390 }
391 _writeSimpleObject(value);
392 }
393 }
394
395 @Override
396 public void writeTree(TreeNode rootNode) throws IOException {
397 // As with 'writeObject()', we are not check if write would work
398 if (rootNode == null) {
399 writeNull();
400 } else {
401 if (_objectCodec == null) {
402 throw new IllegalStateException("No ObjectCodec defined");
403 }
404 _objectCodec.writeValue(this, rootNode);
405 }
406 }
407
408 /*
409 /**********************************************************
410 /* Public API, low-level output handling
411 /**********************************************************
412 */
413
414 @Override public abstract void flush() throws IOException;
415 @Override public void close() throws IOException { _closed = true; }
416 @Override public boolean isClosed() { return _closed; }
417
418 /*
419 /**********************************************************
420 /* Package methods for this, sub-classes
421 /**********************************************************
422 */
423
424 /**
425 * Method called to release any buffers generator may be holding,
426 * once generator is being closed.
427 */
428 protected abstract void _releaseBuffers();
429
430 /**
431 * Method called before trying to write a value (scalar or structured),
432 * to verify that this is legal in current output state, as well as to
433 * output separators if and as necessary.
434 *
435 * @param typeMsg Additional message used for generating exception message
436 * if value output is NOT legal in current generator output state.
437 */
438 protected abstract void _verifyValueWrite(String typeMsg) throws IOException;
439
440 /**
441 * Overridable factory method called to instantiate an appropriate {@link PrettyPrinter}
442 * for case of "just use the default one", when {@link #useDefaultPrettyPrinter()} is called.
443 *
444 * @since 2.6
445 */
446 protected PrettyPrinter _constructDefaultPrettyPrinter() {
447 return new DefaultPrettyPrinter();
448 }
449
450 /**
451 * Helper method used to serialize a {@link java.math.BigDecimal} as a String,
452 * for serialization, taking into account configuration settings
453 *
454 * @since 2.7.7
455 */
456 protected String _asString(BigDecimal value) throws IOException {
457 if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) {
458 // 24-Aug-2016, tatu: [core#315] prevent possible DoS vector
459 int scale = value.scale();
460 if ((scale < -MAX_BIG_DECIMAL_SCALE) || (scale > MAX_BIG_DECIMAL_SCALE)) {
461 _reportError(String.format(
462 "Attempt to write plain `java.math.BigDecimal` (see JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) with illegal scale (%d): needs to be between [-%d, %d]",
463 scale, MAX_BIG_DECIMAL_SCALE, MAX_BIG_DECIMAL_SCALE));
464 }
465 return value.toPlainString();
466 }
467 return value.toString();
468 }
469
470 /*
471 /**********************************************************
472 /* UTF-8 related helper method(s)
473 /**********************************************************
474 */
475
476 /**
477 * @since 2.5
478 */
479 protected final int _decodeSurrogate(int surr1, int surr2) throws IOException
480 {
481 // First is known to be valid, but how about the other?
482 if (surr2 < SURR2_FIRST || surr2 > SURR2_LAST) {
483 String msg = "Incomplete surrogate pair: first char 0x"+Integer.toHexString(surr1)+", second 0x"+Integer.toHexString(surr2);
484 _reportError(msg);
485 }
486 int c = 0x10000 + ((surr1 - SURR1_FIRST) << 10) + (surr2 - SURR2_FIRST);
487 return c;
488 }
489 }
490