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") GeneratorBase(int features, ObjectCodec codec)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") GeneratorBase(int features, ObjectCodec codec, JsonWriteContext ctxt)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 */ version()132 @Override public Version version() { return PackageVersion.VERSION; } 133 134 @Override getCurrentValue()135 public Object getCurrentValue() { 136 return _writeContext.getCurrentValue(); 137 } 138 139 @Override setCurrentValue(Object v)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 isEnabled(Feature f)153 @Override public final boolean isEnabled(Feature f) { return (_features & f.getMask()) != 0; } getFeatureMask()154 @Override public int getFeatureMask() { return _features; } 155 156 //public JsonGenerator configure(Feature f, boolean state) { } 157 158 @SuppressWarnings("deprecation") 159 @Override enable(Feature f)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 disable(Feature f)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 setFeatureMask(int newMask)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 overrideStdFeatures(int values, int mask)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") _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures)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 useDefaultPrettyPrinter()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 setCodec(ObjectCodec oc)260 @Override public JsonGenerator setCodec(ObjectCodec oc) { 261 _objectCodec = oc; 262 return this; 263 } 264 getCodec()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 */ getOutputContext()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 writeStartObject(Object forValue)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 writeFieldName(SerializableString name)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 writeString(SerializableString text)321 public void writeString(SerializableString text) throws IOException { 322 writeString(text.getValue()); 323 } 324 writeRawValue(String text)325 @Override public void writeRawValue(String text) throws IOException { 326 _verifyValueWrite("write raw value"); 327 writeRaw(text); 328 } 329 writeRawValue(String text, int offset, int len)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 writeRawValue(char[] text, int offset, int len)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 writeRawValue(SerializableString text)340 @Override public void writeRawValue(SerializableString text) throws IOException { 341 _verifyValueWrite("write raw value"); 342 writeRaw(text); 343 } 344 345 @Override writeBinary(Base64Variant b64variant, InputStream data, int dataLength)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 writeObject(Object value)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 writeTree(TreeNode rootNode)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 flush()414 @Override public abstract void flush() throws IOException; close()415 @Override public void close() throws IOException { _closed = true; } isClosed()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 */ _releaseBuffers()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 */ _verifyValueWrite(String typeMsg)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 */ _constructDefaultPrettyPrinter()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 */ _asString(BigDecimal value)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 */ _decodeSurrogate(int surr1, int surr2)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