New upstream version 2.10.0
Mechtilde
4 years ago
0 | 0 | language: java |
1 | # core streaming still builds on jdk6 and above: | |
2 | # 05-Jan-2018, tatu: Not sure why, but travis build fails on jdk6 so leave it out | |
3 | jdk: | |
4 | - openjdk7 | |
1 | # 08-Dec-2018, tatu: While it should be possible to run core streaming on Java 6, | |
2 | # build won't work with anything below Java 8 now | |
5 | 3 | - openjdk8 |
4 | - oraclejdk11 | |
6 | 5 | |
7 | 6 | # Below this line is configuration for deploying to the Sonatype OSS repo |
8 | 7 | # http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html |
13 | 12 | branches: |
14 | 13 | only: |
15 | 14 | - master |
16 | - "2.9" | |
15 | - "2.10" | |
17 | 16 | |
18 | 17 | # to make jdk6 work, as per: https://github.com/travis-ci/travis-ci/issues/8199 |
19 | 18 | addons: |
0 | This copy of Jackson JSON processor annotations is licensed under the | |
1 | Apache (Software) License, version 2.0 ("the License"). | |
2 | See the License for details about distribution rights, and the | |
3 | specific rights regarding derivate works. | |
4 | ||
5 | You may obtain a copy of the License at: | |
6 | ||
7 | http://www.apache.org/licenses/LICENSE-2.0 |
69 | 69 | JsonFactory factory = objectMapper.getFactory(); |
70 | 70 | ``` |
71 | 71 | |
72 | More information can be found from [Streaming API](http://wiki.fasterxml.com/JacksonStreamingApi | |
73 | ) at Jackson Wiki. | |
74 | ||
75 | 72 | ## Usage, simple reading |
76 | 73 | |
77 | 74 | All reading is by using `JsonParser` (or its sub-classes, in case of data formats other than JSON), |
105 | 102 | * Project [Wiki](../../wiki) has JavaDocs and links to downloadable artifacts |
106 | 103 | * [Jackson Github Hub](https://github.com/FasterXML/jackson) has links to all official Jackson components |
107 | 104 | * [Jackson Github Doc](https://github.com/FasterXML/jackson-docs) is the hub for official Jackson documentation |
108 | * [FasterXML Jackson Project Wiki](http://wiki.fasterxml.com/JacksonHome) has additional documentation (especially for older Jackson versions) | |
109 | * Commercial support (including alternative licensing arrangements) is available by [FasterXML.com](http://fasterxml.com) | |
110 | 105 |
1 | 1 | <modelVersion>4.0.0</modelVersion> |
2 | 2 | <parent> |
3 | 3 | <groupId>com.fasterxml.jackson</groupId> |
4 | <!-- For 2.9.2 and beyond, new parent pom; extends jackson-bom --> | |
5 | 4 | <artifactId>jackson-base</artifactId> |
6 | <version>2.9.9</version> | |
5 | <version>2.10.0</version> | |
7 | 6 | </parent> |
8 | 7 | |
9 | 8 | <groupId>com.fasterxml.jackson.core</groupId> |
10 | 9 | <artifactId>jackson-core</artifactId> |
11 | 10 | <name>Jackson-core</name> |
12 | <version>2.9.9</version> | |
11 | <version>2.10.0</version> | |
13 | 12 | <packaging>bundle</packaging> |
14 | 13 | <description>Core Jackson processing abstractions (aka Streaming API), implementation for JSON</description> |
15 | 14 | <inceptionYear>2008</inceptionYear> |
19 | 18 | <connection>scm:git:git@github.com:FasterXML/jackson-core.git</connection> |
20 | 19 | <developerConnection>scm:git:git@github.com:FasterXML/jackson-core.git</developerConnection> |
21 | 20 | <url>http://github.com/FasterXML/jackson-core</url> |
22 | <tag>jackson-core-2.9.9</tag> | |
21 | <tag>jackson-core-2.10.0</tag> | |
23 | 22 | </scm> |
24 | 23 | |
25 | 24 | <properties> |
26 | <!-- 16-Sep-2016, tatu: Retain Java6/JDK1.6 compatibility for streaming for Jackson 2.x --> | |
25 | <!-- 04-Mar-2019, tatu: Retain Java6/JDK1.6 compatibility for annotations for Jackson 2.x, | |
26 | but use Moditect to get JDK9+ module info support; need newer bundle plugin as well | |
27 | --> | |
27 | 28 | <javac.src.version>1.6</javac.src.version> |
28 | 29 | <javac.target.version>1.6</javac.target.version> |
29 | 30 | |
30 | <!-- 04-May-2016, tatu: Bundle-plugin 3.x seems to require Java 7, so to | |
31 | build for Java 6 need to downgrade here to last working 2.x version | |
32 | (2.5.4 had some issues wrt shading) | |
33 | --> | |
34 | <version.plugin.bundle>2.5.3</version.plugin.bundle> | |
31 | <maven.compiler.source>1.6</maven.compiler.source> | |
32 | <maven.compiler.target>1.6</maven.compiler.target> | |
35 | 33 | |
36 | 34 | <osgi.export>com.fasterxml.jackson.core;version=${project.version}, |
37 | 35 | com.fasterxml.jackson.core.*;version=${project.version} |
40 | 38 | <!-- Generate PackageVersion.java into this directory. --> |
41 | 39 | <packageVersion.dir>com/fasterxml/jackson/core/json</packageVersion.dir> |
42 | 40 | <packageVersion.package>${project.groupId}.json</packageVersion.package> |
43 | <!-- usually above is fine for Automatic Module Name, but not here: --> | |
44 | <jdk.module.name>com.fasterxml.jackson.core</jdk.module.name> | |
45 | 41 | </properties> |
46 | 42 | |
47 | 43 | <!-- Alas, need to include snapshot reference since otherwise can not find |
58 | 54 | |
59 | 55 | <build> |
60 | 56 | <plugins> |
57 | ||
58 | <!-- 26-Aug-2019, tatu: JaCoCo for code coverage --> | |
59 | <plugin> | |
60 | <groupId>org.jacoco</groupId> | |
61 | <artifactId>jacoco-maven-plugin</artifactId> | |
62 | <version>0.8.4</version> | |
63 | <executions> | |
64 | <execution> | |
65 | <goals> | |
66 | <goal>prepare-agent</goal> | |
67 | </goals> | |
68 | </execution> | |
69 | <execution> | |
70 | <id>report</id> | |
71 | <phase>test</phase> | |
72 | <goals> | |
73 | <goal>report</goal> | |
74 | </goals> | |
75 | </execution> | |
76 | </executions> | |
77 | </plugin> | |
78 | ||
61 | 79 | <!-- Important: enable enforcer plug-in: --> |
62 | 80 | <plugin> |
63 | 81 | <artifactId>maven-enforcer-plugin</artifactId> |
95 | 113 | <groupId>com.google.code.maven-replacer-plugin</groupId> |
96 | 114 | <artifactId>replacer</artifactId> |
97 | 115 | </plugin> |
116 | ||
117 | <!-- 04-Mar-2019, tatu: Add rudimentary JDK9+ module info. To build with JDK 8 | |
118 | will have to use `moduleInfoFile` as anything else requires JDK 9+ | |
119 | --> | |
120 | <plugin> | |
121 | <groupId>org.moditect</groupId> | |
122 | <artifactId>moditect-maven-plugin</artifactId> | |
123 | </plugin> | |
98 | 124 | </plugins> |
99 | 125 | </build> |
100 | 126 |
154 | 154 | * Suggested #463: Ensure that `skipChildren()` of non-blocking `JsonParser` will throw |
155 | 155 | exception if not enough input |
156 | 156 | (2.9.6) |
157 | * Reported, Contributed test for #563: Async parser does not keep track of Array context properly | |
158 | (2.10.0) | |
157 | 159 | |
158 | 160 | Alexander Eyers-Taylor (aeyerstaylor@github) |
159 | 161 | * Reported #510: Fix ArrayIndexOutofBoundsException found by LGTM.com |
162 | 164 | Henrik Gustafsson (gsson@github) |
163 | 165 | * Reported #516: _inputPtr off-by-one in UTF8StreamJsonParser._parseNumber2() |
164 | 166 | (2.9.9) |
167 | ||
168 | Alex Rebert (alpire@github) | |
169 | * Reported #540, suggested fix: UTF8StreamJsonParser: fix byte to int conversion for | |
170 | malformed escapes | |
171 | (2.9.10) | |
172 | * Reported #547: `CharsToNameCanonicalizer`: Internal error on `SymbolTable.rehash()` with high | |
173 | number of hash collisions | |
174 | (2.10.0) | |
175 | * Reported #548: ByteQuadsCanonicalizer: ArrayIndexOutOfBoundsException in addName | |
176 | (2.10.0) | |
177 | ||
178 | Sam Smith (Oracle Security Researcher) | |
179 | * Reported #540 (concurrently with Alex R, before fix was included) | |
180 | (2.9.10) | |
181 | ||
182 | Philippe Marschall (marschall@github) | |
183 | * Requested #480: `SerializableString` value can not directly render to Writer | |
184 | (2.10.0) | |
185 | ||
186 | David Nault (dnault@github) | |
187 | * Reported #531: Non-blocking parser reports incorrect locations when fed with | |
188 | non-zero offset | |
189 | (2.10.0) | |
190 | ||
191 | Fabien Renaud (fabienrenaud@github) | |
192 | * Reported, contributed fix fir #533: UTF-8 BOM not accounted for in | |
193 | `JsonLocation.getByteOffset()` | |
194 | (2.10.0) |
12 | 12 | ------------------------------------------------------------------------ |
13 | 13 | === Releases === |
14 | 14 | ------------------------------------------------------------------------ |
15 | ||
16 | 2.10.0 (26-Sep-2019) | |
17 | ||
18 | #433: Add Builder pattern for creating configured Stream factories | |
19 | #464: Add "maximum unescaped char" configuration option for `JsonFactory` via builder | |
20 | #467: Create `JsonReadFeature` to move JSON-specific `JsonParser.Feature`s to | |
21 | #479: Improve thread-safety of buffer recycling | |
22 | #480: `SerializableString` value can not directly render to Writer | |
23 | (requested by Philippe M) | |
24 | #481: Create `JsonWriteFeature` to move JSON-specific `JsonGenerator.Feature`s to | |
25 | #484: Implement `UTF8JsonGenerator.writeRawValue(SerializableString)` (and | |
26 | `writeRaw(..)`) more efficiently | |
27 | #495: Create `StreamReadFeature` to move non-json specific `JsonParser.Feature`s to | |
28 | #496: Create `StreamWriteFeature` to take over non-json-specific `JsonGenerator.Feature`s | |
29 | #502: Make `DefaultPrettyPrinter.createInstance()` to fail for sub-classes | |
30 | #506: Add missing type parameter for `TypeReference` in `ObjectCodec` | |
31 | #508: Add new exception type `InputCoercionException` to be used for failed coercions | |
32 | like overflow for `int` | |
33 | #517: Add `JsonGenerator.writeStartObject(Object, int)` (needed by CBOR, maybe Avro) | |
34 | #527: Add simple module-info for JDK9+, using Moditect | |
35 | #533: UTF-8 BOM not accounted for in JsonLocation.getByteOffset() | |
36 | (contributed by Fabien R) | |
37 | #539: Reduce max size of recycled byte[]/char[] blocks by `TextBuffer`, `ByteArrayBuilder` | |
38 | #547: `CharsToNameCanonicalizer`: Internal error on `SymbolTable.rehash()` with high | |
39 | number of hash collisions | |
40 | (reported by Alex R) | |
41 | #548: ByteQuadsCanonicalizer: ArrayIndexOutOfBoundsException in addName | |
42 | (reported by Alex R) | |
43 | #549: Add configurability of "quote character" for JSON factory | |
44 | #561: Misleading exception for unquoted String parsing | |
45 | #563: Async parser does not keep track of Array context properly | |
46 | (reported by Doug R) | |
47 | - Rewrite `JsonGenerator.copyCurrentStructure()` to remove recursion) | |
48 | - Add `missingNode()`, `nullNode()` in `TreeCodec` | |
49 | - Add `JsonParserDelegate.delegate()` methods | |
50 | ||
51 | 2.9.10 (21-Sep-2019) | |
52 | ||
53 | #540: UTF8StreamJsonParser: fix byte to int conversion for malformed escapes | |
54 | (reported by Alex R and Sam S) | |
55 | #556: 'IndexOutOfBoundsException' in UTF8JsonGenerator.writeString(Reader, len) | |
56 | when using a negative length | |
57 | (reported by jacob-alan-ward@github) | |
15 | 58 | |
16 | 59 | 2.9.9 (16-May-2019) |
17 | 60 |
87 | 87 | private final transient boolean _usesPadding; |
88 | 88 | |
89 | 89 | /** |
90 | * Characted used for padding, if any ({@link #PADDING_CHAR_NONE} if not). | |
90 | * Character used for padding, if any ({@link #PADDING_CHAR_NONE} if not). | |
91 | 91 | */ |
92 | 92 | private final transient char _paddingChar; |
93 | 93 | |
366 | 366 | |
367 | 367 | /** |
368 | 368 | * Convenience method for converting given byte array as base64 encoded String |
369 | * using this variant's settings, | |
370 | * optionally enclosed in double-quotes. | |
369 | * using this variant's settings, optionally enclosed in double-quotes. | |
370 | * Linefeeds added, if needed, are expressed as 2-character JSON (and Java source) | |
371 | * escape sequence of backslash + `n`. | |
371 | 372 | * |
372 | 373 | * @param input Byte array to encode |
373 | 374 | * @param addQuotes Whether to surround resulting value in double quotes or not |
374 | 375 | */ |
375 | 376 | public String encode(byte[] input, boolean addQuotes) |
376 | 377 | { |
377 | int inputEnd = input.length; | |
378 | StringBuilder sb; | |
379 | { | |
380 | // let's approximate... 33% overhead, ~= 3/8 (0.375) | |
381 | int outputLen = inputEnd + (inputEnd >> 2) + (inputEnd >> 3); | |
382 | sb = new StringBuilder(outputLen); | |
383 | } | |
378 | final int inputEnd = input.length; | |
379 | final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3)); | |
384 | 380 | if (addQuotes) { |
385 | 381 | sb.append('"'); |
386 | 382 | } |
422 | 418 | } |
423 | 419 | |
424 | 420 | /** |
421 | * Convenience method for converting given byte array as base64 encoded String | |
422 | * using this variant's settings, optionally enclosed in double-quotes. | |
423 | * Linefeed character to use is passed explicitly. | |
424 | * | |
425 | * @param input Byte array to encode | |
426 | * @param addQuotes Whether to surround resulting value in double quotes or not | |
427 | * | |
428 | * @since 2.10 | |
429 | */ | |
430 | public String encode(byte[] input, boolean addQuotes, String linefeed) | |
431 | { | |
432 | final int inputEnd = input.length; | |
433 | final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3)); | |
434 | if (addQuotes) { | |
435 | sb.append('"'); | |
436 | } | |
437 | ||
438 | int chunksBeforeLF = getMaxLineLength() >> 2; | |
439 | ||
440 | int inputPtr = 0; | |
441 | int safeInputEnd = inputEnd-3; | |
442 | ||
443 | while (inputPtr <= safeInputEnd) { | |
444 | int b24 = ((int) input[inputPtr++]) << 8; | |
445 | b24 |= ((int) input[inputPtr++]) & 0xFF; | |
446 | b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); | |
447 | encodeBase64Chunk(sb, b24); | |
448 | if (--chunksBeforeLF <= 0) { | |
449 | sb.append(linefeed); | |
450 | chunksBeforeLF = getMaxLineLength() >> 2; | |
451 | } | |
452 | } | |
453 | int inputLeft = inputEnd - inputPtr; | |
454 | if (inputLeft > 0) { | |
455 | int b24 = ((int) input[inputPtr++]) << 16; | |
456 | if (inputLeft == 2) { | |
457 | b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; | |
458 | } | |
459 | encodeBase64Partial(sb, b24, inputLeft); | |
460 | } | |
461 | ||
462 | if (addQuotes) { | |
463 | sb.append('"'); | |
464 | } | |
465 | return sb.toString(); | |
466 | } | |
467 | ||
468 | /** | |
425 | 469 | * Convenience method for decoding contents of a Base64-encoded String, |
426 | 470 | * using this variant's settings. |
427 | * | |
471 | * | |
428 | 472 | * @param input |
429 | * | |
430 | * @since 2.2.3 | |
473 | * | |
474 | * @since 2.3 | |
431 | 475 | * |
432 | 476 | * @throws IllegalArgumentException if input is not valid base64 encoded data |
433 | 477 | */ |
40 | 40 | */ |
41 | 41 | @SuppressWarnings("resource") |
42 | 42 | public class JsonFactory |
43 | extends TokenStreamFactory | |
43 | 44 | implements Versioned, |
44 | 45 | java.io.Serializable // since 2.1 (for Android, mostly) |
45 | 46 | { |
46 | private static final long serialVersionUID = 1; // since 2.6.0 | |
47 | private static final long serialVersionUID = 2; | |
47 | 48 | |
48 | 49 | /* |
49 | 50 | /********************************************************** |
174 | 175 | */ |
175 | 176 | protected final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults(); |
176 | 177 | |
177 | private final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; | |
178 | ||
178 | public final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; | |
179 | ||
180 | /** | |
181 | * @since 2.10 | |
182 | */ | |
183 | public final static char DEFAULT_QUOTE_CHAR = '"'; | |
184 | ||
179 | 185 | /* |
180 | 186 | /********************************************************** |
181 | 187 | /* Buffer, symbol table management |
195 | 201 | *<p> |
196 | 202 | * TODO: should clean up this; looks messy having 2 alternatives |
197 | 203 | * with not very clear differences. |
198 | * | |
199 | * @since 2.6.0 | |
204 | * | |
205 | * @since 2.6 | |
200 | 206 | */ |
201 | 207 | protected final transient ByteQuadsCanonicalizer _byteSymbolCanonicalizer = ByteQuadsCanonicalizer.createRoot(); |
202 | 208 | |
203 | 209 | /* |
204 | 210 | /********************************************************** |
205 | /* Configuration | |
211 | /* Configuration, simple feature flags | |
212 | /********************************************************** | |
213 | */ | |
214 | ||
215 | /** | |
216 | * Currently enabled factory features. | |
217 | */ | |
218 | protected int _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS; | |
219 | ||
220 | /** | |
221 | * Currently enabled parser features. | |
222 | */ | |
223 | protected int _parserFeatures = DEFAULT_PARSER_FEATURE_FLAGS; | |
224 | ||
225 | /** | |
226 | * Currently enabled generator features. | |
227 | */ | |
228 | protected int _generatorFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS; | |
229 | ||
230 | /* | |
231 | /********************************************************** | |
232 | /* Configuration, helper objects | |
206 | 233 | /********************************************************** |
207 | 234 | */ |
208 | 235 | |
216 | 243 | protected ObjectCodec _objectCodec; |
217 | 244 | |
218 | 245 | /** |
219 | * Currently enabled factory features. | |
220 | */ | |
221 | protected int _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS; | |
222 | ||
223 | /** | |
224 | * Currently enabled parser features. | |
225 | */ | |
226 | protected int _parserFeatures = DEFAULT_PARSER_FEATURE_FLAGS; | |
227 | ||
228 | /** | |
229 | * Currently enabled generator features. | |
230 | */ | |
231 | protected int _generatorFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS; | |
232 | ||
233 | /** | |
234 | 246 | * Definition of custom character escapes to use for generators created |
235 | 247 | * by this factory, if any. If null, standard data format specific |
236 | 248 | * escapes are used. |
257 | 269 | * @since 2.1 |
258 | 270 | */ |
259 | 271 | protected SerializableString _rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR; |
260 | ||
272 | ||
273 | /** | |
274 | * Optional threshold used for automatically escaping character above certain character | |
275 | * code value: either {@code 0} to indicate that no threshold is specified, or value | |
276 | * at or above 127 to indicate last character code that is NOT automatically escaped | |
277 | * (but depends on other configuration rules for checking). | |
278 | * | |
279 | * @since 2.10 | |
280 | */ | |
281 | protected int _maximumNonEscapedChar; | |
282 | ||
283 | /** | |
284 | * Character used for quoting field names (if field name quoting has not | |
285 | * been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES}) | |
286 | * and JSON String values. | |
287 | */ | |
288 | protected final char _quoteChar; | |
289 | ||
261 | 290 | /* |
262 | 291 | /********************************************************** |
263 | 292 | /* Construction |
274 | 303 | * and this reuse only works within context of a single |
275 | 304 | * factory instance. |
276 | 305 | */ |
277 | public JsonFactory() { this(null); } | |
278 | ||
279 | public JsonFactory(ObjectCodec oc) { _objectCodec = oc; } | |
306 | public JsonFactory() { this((ObjectCodec) null); } | |
307 | ||
308 | public JsonFactory(ObjectCodec oc) { | |
309 | _objectCodec = oc; | |
310 | _quoteChar = DEFAULT_QUOTE_CHAR; | |
311 | } | |
280 | 312 | |
281 | 313 | /** |
282 | 314 | * Constructor used when copy()ing a factory instance. |
286 | 318 | protected JsonFactory(JsonFactory src, ObjectCodec codec) |
287 | 319 | { |
288 | 320 | _objectCodec = codec; |
321 | ||
322 | // General | |
289 | 323 | _factoryFeatures = src._factoryFeatures; |
290 | 324 | _parserFeatures = src._parserFeatures; |
291 | 325 | _generatorFeatures = src._generatorFeatures; |
292 | _characterEscapes = src._characterEscapes; | |
293 | 326 | _inputDecorator = src._inputDecorator; |
294 | 327 | _outputDecorator = src._outputDecorator; |
328 | ||
329 | // JSON-specific | |
330 | _characterEscapes = src._characterEscapes; | |
295 | 331 | _rootValueSeparator = src._rootValueSeparator; |
296 | ||
297 | /* 27-Apr-2013, tatu: How about symbol table; should we try to | |
298 | * reuse shared symbol tables? Could be more efficient that way; | |
299 | * although can slightly add to concurrency overhead. | |
300 | */ | |
301 | } | |
302 | ||
332 | _maximumNonEscapedChar = src._maximumNonEscapedChar; | |
333 | _quoteChar = src._quoteChar; | |
334 | } | |
335 | ||
336 | /** | |
337 | * Constructor used by {@link JsonFactoryBuilder} for instantiation. | |
338 | * | |
339 | * @since 2.10 | |
340 | */ | |
341 | public JsonFactory(JsonFactoryBuilder b) { | |
342 | _objectCodec = null; | |
343 | ||
344 | // General | |
345 | _factoryFeatures = b._factoryFeatures; | |
346 | _parserFeatures = b._streamReadFeatures; | |
347 | _generatorFeatures = b._streamWriteFeatures; | |
348 | _inputDecorator = b._inputDecorator; | |
349 | _outputDecorator = b._outputDecorator; | |
350 | ||
351 | // JSON-specific | |
352 | _characterEscapes = b._characterEscapes; | |
353 | _rootValueSeparator = b._rootValueSeparator; | |
354 | _maximumNonEscapedChar = b._maximumNonEscapedChar; | |
355 | _quoteChar = b._quoteChar; | |
356 | } | |
357 | ||
358 | /** | |
359 | * Constructor for subtypes; needed to work around the fact that before 3.0, | |
360 | * this factory has cumbersome dual role as generic type as well as actual | |
361 | * implementation for json. | |
362 | * | |
363 | * @param b Builder that contains information | |
364 | * @param bogus Argument only needed to separate constructor signature; ignored | |
365 | */ | |
366 | protected JsonFactory(TSFBuilder<?,?> b, boolean bogus) { | |
367 | _objectCodec = null; | |
368 | ||
369 | _factoryFeatures = b._factoryFeatures; | |
370 | _parserFeatures = b._streamReadFeatures; | |
371 | _generatorFeatures = b._streamWriteFeatures; | |
372 | _inputDecorator = b._inputDecorator; | |
373 | _outputDecorator = b._outputDecorator; | |
374 | ||
375 | // JSON-specific: need to assign even if not really used | |
376 | _characterEscapes = null; | |
377 | _rootValueSeparator = null; | |
378 | _maximumNonEscapedChar = 0; | |
379 | _quoteChar = DEFAULT_QUOTE_CHAR; | |
380 | } | |
381 | ||
382 | /** | |
383 | * Method that allows construction of differently configured factory, starting | |
384 | * with settings of this factory. | |
385 | * | |
386 | * @since 2.10 | |
387 | */ | |
388 | public TSFBuilder<?,?> rebuild() { | |
389 | // 13-Jun-2018, tatu: Verify sub-classing to prevent strange bugs in format impls | |
390 | _requireJSONFactory("Factory implementation for format (%s) MUST override `rebuild()` method"); | |
391 | return new JsonFactoryBuilder(this); | |
392 | } | |
393 | ||
394 | /** | |
395 | * Main factory method to use for constructing {@link JsonFactory} instances with | |
396 | * different configuration: creates and returns a builder for collecting configuration | |
397 | * settings; instance created by calling {@code build()} after all configuration | |
398 | * set. | |
399 | *<p> | |
400 | * NOTE: signature unfortunately does not expose true implementation type; this | |
401 | * will be fixed in 3.0. | |
402 | */ | |
403 | public static TSFBuilder<?,?> builder() { | |
404 | return new JsonFactoryBuilder(); | |
405 | } | |
406 | ||
303 | 407 | /** |
304 | 408 | * Method for constructing a new {@link JsonFactory} that has |
305 | 409 | * the same settings as this instance, but is otherwise |
320 | 424 | // as per above, do clear ObjectCodec |
321 | 425 | return new JsonFactory(this, null); |
322 | 426 | } |
323 | ||
324 | /** | |
325 | * @since 2.1 | |
326 | * @param exp | |
427 | ||
428 | /** | |
429 | * @since 2.1 | |
327 | 430 | */ |
328 | 431 | protected void _checkInvalidCopy(Class<?> exp) |
329 | 432 | { |
353 | 456 | /* Capability introspection |
354 | 457 | /********************************************************** |
355 | 458 | */ |
356 | ||
459 | ||
357 | 460 | /** |
358 | 461 | * Introspection method that higher-level functionality may call |
359 | 462 | * to see whether underlying data format requires a stable ordering |
369 | 472 | * |
370 | 473 | * @since 2.3 |
371 | 474 | */ |
475 | @Override | |
372 | 476 | public boolean requiresPropertyOrdering() { return false; } |
373 | 477 | |
374 | 478 | /** |
383 | 487 | * |
384 | 488 | * @since 2.3 |
385 | 489 | */ |
490 | @Override | |
386 | 491 | public boolean canHandleBinaryNatively() { return false; } |
387 | 492 | |
388 | 493 | /** |
407 | 512 | * |
408 | 513 | * @since 2.9 |
409 | 514 | */ |
515 | @Override | |
410 | 516 | public boolean canParseAsync() { |
411 | 517 | // 31-May-2017, tatu: Jackson 2.9 does support async parsing for JSON, |
412 | 518 | // but not all other formats, so need to do this: |
413 | 519 | return _isJSONFactory(); |
414 | 520 | } |
415 | 521 | |
416 | /** | |
417 | * Method for accessing kind of {@link FormatFeature} that a parser | |
418 | * {@link JsonParser} produced by this factory would accept, if any; | |
419 | * <code>null</code> returned if none. | |
420 | * | |
421 | * @since 2.6 | |
422 | */ | |
522 | @Override | |
423 | 523 | public Class<? extends FormatFeature> getFormatReadFeatureType() { |
424 | 524 | return null; |
425 | 525 | } |
426 | 526 | |
427 | /** | |
428 | * Method for accessing kind of {@link FormatFeature} that a parser | |
429 | * {@link JsonGenerator} produced by this factory would accept, if any; | |
430 | * <code>null</code> returned if none. | |
431 | * | |
432 | * @since 2.6 | |
433 | */ | |
527 | @Override | |
434 | 528 | public Class<? extends FormatFeature> getFormatWriteFeatureType() { |
435 | 529 | return null; |
436 | 530 | } |
531 | ||
437 | 532 | /* |
438 | 533 | /********************************************************** |
439 | 534 | /* Format detection functionality |
450 | 545 | * |
451 | 546 | * @since 2.1 |
452 | 547 | */ |
548 | @Override | |
453 | 549 | public boolean canUseSchema(FormatSchema schema) { |
454 | 550 | if (schema == null){ |
455 | 551 | return false; |
465 | 561 | * Note: sub-classes should override this method; default |
466 | 562 | * implementation will return null for all sub-classes |
467 | 563 | */ |
564 | @Override | |
468 | 565 | public String getFormatName() |
469 | 566 | { |
470 | 567 | /* Somewhat nasty check: since we can't make this abstract |
536 | 633 | /** |
537 | 634 | * Method for enabling or disabling specified parser feature |
538 | 635 | * (check {@link JsonParser.Feature} for list of features) |
539 | */ | |
636 | * | |
637 | * @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead | |
638 | */ | |
639 | @Deprecated | |
540 | 640 | public final JsonFactory configure(JsonFactory.Feature f, boolean state) { |
541 | 641 | return state ? enable(f) : disable(f); |
542 | 642 | } |
544 | 644 | /** |
545 | 645 | * Method for enabling specified parser feature |
546 | 646 | * (check {@link JsonFactory.Feature} for list of features) |
547 | */ | |
647 | * | |
648 | * @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead | |
649 | */ | |
650 | @Deprecated | |
548 | 651 | public JsonFactory enable(JsonFactory.Feature f) { |
549 | 652 | _factoryFeatures |= f.getMask(); |
550 | 653 | return this; |
553 | 656 | /** |
554 | 657 | * Method for disabling specified parser features |
555 | 658 | * (check {@link JsonFactory.Feature} for list of features) |
556 | */ | |
659 | * | |
660 | * @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead | |
661 | */ | |
662 | @Deprecated | |
557 | 663 | public JsonFactory disable(JsonFactory.Feature f) { |
558 | 664 | _factoryFeatures &= ~f.getMask(); |
559 | 665 | return this; |
565 | 671 | public final boolean isEnabled(JsonFactory.Feature f) { |
566 | 672 | return (_factoryFeatures & f.getMask()) != 0; |
567 | 673 | } |
568 | ||
674 | ||
675 | @Override | |
676 | public final int getParserFeatures() { | |
677 | return _parserFeatures; | |
678 | } | |
679 | ||
680 | @Override | |
681 | public final int getGeneratorFeatures() { | |
682 | return _generatorFeatures; | |
683 | } | |
684 | ||
685 | // MUST be overridden by sub-classes that support format-specific parser features | |
686 | @Override | |
687 | public int getFormatParserFeatures() { | |
688 | return 0; | |
689 | } | |
690 | ||
691 | // MUST be overridden by sub-classes that support format-specific generator features | |
692 | @Override | |
693 | public int getFormatGeneratorFeatures() { | |
694 | return 0; | |
695 | } | |
696 | ||
569 | 697 | /* |
570 | 698 | /********************************************************** |
571 | 699 | /* Configuration, parser configuration |
572 | 700 | /********************************************************** |
573 | 701 | */ |
574 | ||
702 | ||
575 | 703 | /** |
576 | 704 | * Method for enabling or disabling specified parser feature |
577 | 705 | * (check {@link JsonParser.Feature} for list of features) |
601 | 729 | /** |
602 | 730 | * Checked whether specified parser feature is enabled. |
603 | 731 | */ |
732 | @Override | |
604 | 733 | public final boolean isEnabled(JsonParser.Feature f) { |
605 | 734 | return (_parserFeatures & f.getMask()) != 0; |
606 | 735 | } |
607 | 736 | |
608 | 737 | /** |
738 | * @since 2.10 | |
739 | */ | |
740 | public final boolean isEnabled(StreamReadFeature f) { | |
741 | return (_parserFeatures & f.mappedFeature().getMask()) != 0; | |
742 | } | |
743 | ||
744 | /** | |
609 | 745 | * Method for getting currently configured input decorator (if any; |
610 | 746 | * there is no default decorator). |
611 | 747 | */ |
615 | 751 | |
616 | 752 | /** |
617 | 753 | * Method for overriding currently configured input decorator |
618 | */ | |
754 | * | |
755 | * @deprecated Since 2.10 use {@link JsonFactoryBuilder#inputDecorator(InputDecorator)} instead | |
756 | */ | |
757 | @Deprecated | |
619 | 758 | public JsonFactory setInputDecorator(InputDecorator d) { |
620 | 759 | _inputDecorator = d; |
621 | 760 | return this; |
635 | 774 | return state ? enable(f) : disable(f); |
636 | 775 | } |
637 | 776 | |
638 | ||
639 | 777 | /** |
640 | 778 | * Method for enabling specified generator features |
641 | 779 | * (check {@link JsonGenerator.Feature} for list of features) |
657 | 795 | /** |
658 | 796 | * Check whether specified generator feature is enabled. |
659 | 797 | */ |
798 | @Override | |
660 | 799 | public final boolean isEnabled(JsonGenerator.Feature f) { |
661 | 800 | return (_generatorFeatures & f.getMask()) != 0; |
662 | 801 | } |
663 | 802 | |
803 | /** | |
804 | * @since 2.10 | |
805 | */ | |
806 | public final boolean isEnabled(StreamWriteFeature f) { | |
807 | return (_generatorFeatures & f.mappedFeature().getMask()) != 0; | |
808 | } | |
809 | ||
664 | 810 | /** |
665 | 811 | * Method for accessing custom escapes factory uses for {@link JsonGenerator}s |
666 | 812 | * it creates. |
686 | 832 | |
687 | 833 | /** |
688 | 834 | * Method for overriding currently configured output decorator |
689 | */ | |
835 | * | |
836 | * @deprecated Since 2.10 use {@link JsonFactoryBuilder#inputDecorator(InputDecorator)} instead | |
837 | */ | |
838 | @Deprecated | |
690 | 839 | public JsonFactory setOutputDecorator(OutputDecorator d) { |
691 | 840 | _outputDecorator = d; |
692 | 841 | return this; |
759 | 908 | * |
760 | 909 | * @since 2.1 |
761 | 910 | */ |
911 | @Override | |
762 | 912 | public JsonParser createParser(File f) throws IOException, JsonParseException { |
763 | 913 | // true, since we create InputStream from File |
764 | 914 | IOContext ctxt = _createContext(f, true); |
786 | 936 | * |
787 | 937 | * @since 2.1 |
788 | 938 | */ |
939 | @Override | |
789 | 940 | public JsonParser createParser(URL url) throws IOException, JsonParseException { |
790 | 941 | // true, since we create InputStream from URL |
791 | 942 | IOContext ctxt = _createContext(url, true); |
800 | 951 | * The input stream will <b>not be owned</b> by |
801 | 952 | * the parser, it will still be managed (i.e. closed if |
802 | 953 | * end-of-stream is reacher, or parser close method called) |
803 | * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE} | |
954 | * if (and only if) {@link com.fasterxml.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE} | |
804 | 955 | * is enabled. |
805 | 956 | *<p> |
806 | 957 | * |
814 | 965 | * |
815 | 966 | * @since 2.1 |
816 | 967 | */ |
968 | @Override | |
817 | 969 | public JsonParser createParser(InputStream in) throws IOException, JsonParseException { |
818 | 970 | IOContext ctxt = _createContext(in, false); |
819 | 971 | return _createParser(_decorate(in, ctxt), ctxt); |
820 | 972 | } |
821 | 973 | |
974 | /** | |
975 | * Method for constructing parser for parsing | |
976 | * the contents accessed via specified Reader. | |
977 | <p> | |
978 | * The read stream will <b>not be owned</b> by | |
979 | * the parser, it will still be managed (i.e. closed if | |
980 | * end-of-stream is reacher, or parser close method called) | |
981 | * if (and only if) {@link com.fasterxml.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE} | |
982 | * is enabled. | |
983 | * | |
984 | * @param r Reader to use for reading JSON content to parse | |
985 | * | |
986 | * @since 2.1 | |
987 | */ | |
988 | @Override | |
989 | public JsonParser createParser(Reader r) throws IOException, JsonParseException { | |
990 | // false -> we do NOT own Reader (did not create it) | |
991 | IOContext ctxt = _createContext(r, false); | |
992 | return _createParser(_decorate(r, ctxt), ctxt); | |
993 | } | |
994 | ||
995 | /** | |
996 | * Method for constructing parser for parsing | |
997 | * the contents of given byte array. | |
998 | * | |
999 | * @since 2.1 | |
1000 | */ | |
1001 | @Override | |
1002 | public JsonParser createParser(byte[] data) throws IOException, JsonParseException { | |
1003 | IOContext ctxt = _createContext(data, true); | |
1004 | if (_inputDecorator != null) { | |
1005 | InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); | |
1006 | if (in != null) { | |
1007 | return _createParser(in, ctxt); | |
1008 | } | |
1009 | } | |
1010 | return _createParser(data, 0, data.length, ctxt); | |
1011 | } | |
1012 | ||
1013 | /** | |
1014 | * Method for constructing parser for parsing | |
1015 | * the contents of given byte array. | |
1016 | * | |
1017 | * @param data Buffer that contains data to parse | |
1018 | * @param offset Offset of the first data byte within buffer | |
1019 | * @param len Length of contents to parse within buffer | |
1020 | * | |
1021 | * @since 2.1 | |
1022 | */ | |
1023 | @Override | |
1024 | public JsonParser createParser(byte[] data, int offset, int len) throws IOException, JsonParseException { | |
1025 | IOContext ctxt = _createContext(data, true); | |
1026 | // [JACKSON-512]: allow wrapping with InputDecorator | |
1027 | if (_inputDecorator != null) { | |
1028 | InputStream in = _inputDecorator.decorate(ctxt, data, offset, len); | |
1029 | if (in != null) { | |
1030 | return _createParser(in, ctxt); | |
1031 | } | |
1032 | } | |
1033 | return _createParser(data, offset, len, ctxt); | |
1034 | } | |
1035 | ||
1036 | /** | |
1037 | * Method for constructing parser for parsing | |
1038 | * contents of given String. | |
1039 | * | |
1040 | * @since 2.1 | |
1041 | */ | |
1042 | @Override | |
1043 | public JsonParser createParser(String content) throws IOException, JsonParseException { | |
1044 | final int strLen = content.length(); | |
1045 | // Actually, let's use this for medium-sized content, up to 64kB chunk (32kb char) | |
1046 | if ((_inputDecorator != null) || (strLen > 0x8000) || !canUseCharArrays()) { | |
1047 | // easier to just wrap in a Reader than extend InputDecorator; or, if content | |
1048 | // is too long for us to copy it over | |
1049 | return createParser(new StringReader(content)); | |
1050 | } | |
1051 | IOContext ctxt = _createContext(content, true); | |
1052 | char[] buf = ctxt.allocTokenBuffer(strLen); | |
1053 | content.getChars(0, strLen, buf, 0); | |
1054 | return _createParser(buf, 0, strLen, ctxt, true); | |
1055 | } | |
1056 | ||
1057 | /** | |
1058 | * Method for constructing parser for parsing | |
1059 | * contents of given char array. | |
1060 | * | |
1061 | * @since 2.4 | |
1062 | */ | |
1063 | @Override | |
1064 | public JsonParser createParser(char[] content) throws IOException { | |
1065 | return createParser(content, 0, content.length); | |
1066 | } | |
1067 | ||
1068 | /** | |
1069 | * Method for constructing parser for parsing contents of given char array. | |
1070 | * | |
1071 | * @since 2.4 | |
1072 | */ | |
1073 | @Override | |
1074 | public JsonParser createParser(char[] content, int offset, int len) throws IOException { | |
1075 | if (_inputDecorator != null) { // easier to just wrap in a Reader than extend InputDecorator | |
1076 | return createParser(new CharArrayReader(content, offset, len)); | |
1077 | } | |
1078 | return _createParser(content, offset, len, _createContext(content, true), | |
1079 | // important: buffer is NOT recyclable, as it's from caller | |
1080 | false); | |
1081 | } | |
1082 | ||
1083 | /** | |
1084 | * Optional method for constructing parser for reading contents from specified {@link DataInput} | |
1085 | * instance. | |
1086 | *<p> | |
1087 | * If this factory does not support {@link DataInput} as source, | |
1088 | * will throw {@link UnsupportedOperationException} | |
1089 | * | |
1090 | * @since 2.8 | |
1091 | */ | |
1092 | @Override | |
1093 | public JsonParser createParser(DataInput in) throws IOException { | |
1094 | IOContext ctxt = _createContext(in, false); | |
1095 | return _createParser(_decorate(in, ctxt), ctxt); | |
1096 | } | |
1097 | ||
1098 | /* | |
1099 | /********************************************************** | |
1100 | /* Parser factories, non-blocking (async) sources | |
1101 | /********************************************************** | |
1102 | */ | |
1103 | ||
1104 | /** | |
1105 | * Optional method for constructing parser for non-blocking parsing | |
1106 | * via {@link com.fasterxml.jackson.core.async.ByteArrayFeeder} | |
1107 | * interface (accessed using {@link JsonParser#getNonBlockingInputFeeder()} | |
1108 | * from constructed instance). | |
1109 | *<p> | |
1110 | * If this factory does not support non-blocking parsing (either at all, | |
1111 | * or from byte array), | |
1112 | * will throw {@link UnsupportedOperationException} | |
1113 | * | |
1114 | * @since 2.9 | |
1115 | */ | |
1116 | @Override | |
1117 | public JsonParser createNonBlockingByteArrayParser() throws IOException | |
1118 | { | |
1119 | // 17-May-2017, tatu: Need to take care not to accidentally create JSON parser | |
1120 | // for non-JSON input: | |
1121 | _requireJSONFactory("Non-blocking source not (yet?) supported for this format (%s)"); | |
1122 | IOContext ctxt = _createNonBlockingContext(null); | |
1123 | ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures); | |
1124 | return new NonBlockingJsonParser(ctxt, _parserFeatures, can); | |
1125 | } | |
1126 | ||
1127 | /* | |
1128 | /********************************************************** | |
1129 | /* Generator factories | |
1130 | /********************************************************** | |
1131 | */ | |
1132 | ||
1133 | /** | |
1134 | * Method for constructing JSON generator for writing JSON content | |
1135 | * using specified output stream. | |
1136 | * Encoding to use must be specified, and needs to be one of available | |
1137 | * types (as per JSON specification). | |
1138 | *<p> | |
1139 | * Underlying stream <b>is NOT owned</b> by the generator constructed, | |
1140 | * so that generator will NOT close the output stream when | |
1141 | * {@link JsonGenerator#close} is called (unless auto-closing | |
1142 | * feature, | |
1143 | * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} | |
1144 | * is enabled). | |
1145 | * Using application needs to close it explicitly if this is the case. | |
1146 | *<p> | |
1147 | * Note: there are formats that use fixed encoding (like most binary data formats) | |
1148 | * and that ignore passed in encoding. | |
1149 | * | |
1150 | * @param out OutputStream to use for writing JSON content | |
1151 | * @param enc Character encoding to use | |
1152 | * | |
1153 | * @since 2.1 | |
1154 | */ | |
1155 | @Override | |
1156 | public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) | |
1157 | throws IOException | |
1158 | { | |
1159 | // false -> we won't manage the stream unless explicitly directed to | |
1160 | IOContext ctxt = _createContext(out, false); | |
1161 | ctxt.setEncoding(enc); | |
1162 | if (enc == JsonEncoding.UTF8) { | |
1163 | return _createUTF8Generator(_decorate(out, ctxt), ctxt); | |
1164 | } | |
1165 | Writer w = _createWriter(out, enc, ctxt); | |
1166 | return _createGenerator(_decorate(w, ctxt), ctxt); | |
1167 | } | |
1168 | ||
1169 | /** | |
1170 | * Convenience method for constructing generator that uses default | |
1171 | * encoding of the format (UTF-8 for JSON and most other data formats). | |
1172 | *<p> | |
1173 | * Note: there are formats that use fixed encoding (like most binary data formats). | |
1174 | * | |
1175 | * @since 2.1 | |
1176 | */ | |
1177 | @Override | |
1178 | public JsonGenerator createGenerator(OutputStream out) throws IOException { | |
1179 | return createGenerator(out, JsonEncoding.UTF8); | |
1180 | } | |
1181 | ||
1182 | /** | |
1183 | * Method for constructing JSON generator for writing JSON content | |
1184 | * using specified Writer. | |
1185 | *<p> | |
1186 | * Underlying stream <b>is NOT owned</b> by the generator constructed, | |
1187 | * so that generator will NOT close the Reader when | |
1188 | * {@link JsonGenerator#close} is called (unless auto-closing | |
1189 | * feature, | |
1190 | * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled). | |
1191 | * Using application needs to close it explicitly. | |
1192 | * | |
1193 | * @since 2.1 | |
1194 | * | |
1195 | * @param w Writer to use for writing JSON content | |
1196 | */ | |
1197 | @Override | |
1198 | public JsonGenerator createGenerator(Writer w) throws IOException { | |
1199 | IOContext ctxt = _createContext(w, false); | |
1200 | return _createGenerator(_decorate(w, ctxt), ctxt); | |
1201 | } | |
1202 | ||
1203 | /** | |
1204 | * Method for constructing JSON generator for writing JSON content | |
1205 | * to specified file, overwriting contents it might have (or creating | |
1206 | * it if such file does not yet exist). | |
1207 | * Encoding to use must be specified, and needs to be one of available | |
1208 | * types (as per JSON specification). | |
1209 | *<p> | |
1210 | * Underlying stream <b>is owned</b> by the generator constructed, | |
1211 | * i.e. generator will handle closing of file when | |
1212 | * {@link JsonGenerator#close} is called. | |
1213 | * | |
1214 | * @param f File to write contents to | |
1215 | * @param enc Character encoding to use | |
1216 | * | |
1217 | * @since 2.1 | |
1218 | */ | |
1219 | @Override | |
1220 | public JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOException | |
1221 | { | |
1222 | OutputStream out = new FileOutputStream(f); | |
1223 | // true -> yes, we have to manage the stream since we created it | |
1224 | IOContext ctxt = _createContext(out, true); | |
1225 | ctxt.setEncoding(enc); | |
1226 | if (enc == JsonEncoding.UTF8) { | |
1227 | return _createUTF8Generator(_decorate(out, ctxt), ctxt); | |
1228 | } | |
1229 | Writer w = _createWriter(out, enc, ctxt); | |
1230 | return _createGenerator(_decorate(w, ctxt), ctxt); | |
1231 | } | |
1232 | ||
1233 | /** | |
1234 | * Method for constructing generator for writing content using specified | |
1235 | * {@link DataOutput} instance. | |
1236 | * | |
1237 | * @since 2.8 | |
1238 | */ | |
1239 | @Override | |
1240 | public JsonGenerator createGenerator(DataOutput out, JsonEncoding enc) throws IOException { | |
1241 | return createGenerator(_createDataOutputWrapper(out), enc); | |
1242 | } | |
1243 | ||
1244 | /** | |
1245 | * Convenience method for constructing generator that uses default | |
1246 | * encoding of the format (UTF-8 for JSON and most other data formats). | |
1247 | *<p> | |
1248 | * Note: there are formats that use fixed encoding (like most binary data formats). | |
1249 | * | |
1250 | * @since 2.8 | |
1251 | */ | |
1252 | @Override | |
1253 | public JsonGenerator createGenerator(DataOutput out) throws IOException { | |
1254 | return createGenerator(_createDataOutputWrapper(out), JsonEncoding.UTF8); | |
1255 | } | |
1256 | ||
1257 | /* | |
1258 | /********************************************************** | |
1259 | /* Deprecated parser factory methods: to be removed from 3.x | |
1260 | /********************************************************** | |
1261 | */ | |
1262 | ||
1263 | /** | |
1264 | * Method for constructing JSON parser instance to parse | |
1265 | * contents of specified file. | |
1266 | *<p> | |
1267 | * Encoding is auto-detected from contents according to JSON | |
1268 | * specification recommended mechanism. Json specification | |
1269 | * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, | |
1270 | * so auto-detection implemented only for this charsets. | |
1271 | * For other charsets use {@link #createParser(java.io.Reader)}. | |
1272 | * | |
1273 | *<p> | |
1274 | * Underlying input stream (needed for reading contents) | |
1275 | * will be <b>owned</b> (and managed, i.e. closed as need be) by | |
1276 | * the parser, since caller has no access to it. | |
1277 | * | |
1278 | * @param f File that contains JSON content to parse | |
1279 | * | |
1280 | * @deprecated Since 2.2, use {@link #createParser(File)} instead. | |
1281 | */ | |
1282 | @Deprecated | |
1283 | public JsonParser createJsonParser(File f) throws IOException, JsonParseException { | |
1284 | return createParser(f); | |
1285 | } | |
1286 | ||
1287 | /** | |
1288 | * Method for constructing JSON parser instance to parse | |
1289 | * contents of resource reference by given URL. | |
1290 | * | |
1291 | *<p> | |
1292 | * Encoding is auto-detected from contents according to JSON | |
1293 | * specification recommended mechanism. Json specification | |
1294 | * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, | |
1295 | * so auto-detection implemented only for this charsets. | |
1296 | * For other charsets use {@link #createParser(java.io.Reader)}. | |
1297 | * | |
1298 | *<p> | |
1299 | * Underlying input stream (needed for reading contents) | |
1300 | * will be <b>owned</b> (and managed, i.e. closed as need be) by | |
1301 | * the parser, since caller has no access to it. | |
1302 | * | |
1303 | * @param url URL pointing to resource that contains JSON content to parse | |
1304 | * | |
1305 | * @deprecated Since 2.2, use {@link #createParser(URL)} instead. | |
1306 | */ | |
1307 | @Deprecated | |
1308 | public JsonParser createJsonParser(URL url) throws IOException, JsonParseException { | |
1309 | return createParser(url); | |
1310 | } | |
1311 | ||
1312 | /** | |
1313 | * Method for constructing JSON parser instance to parse | |
1314 | * the contents accessed via specified input stream. | |
1315 | *<p> | |
1316 | * The input stream will <b>not be owned</b> by | |
1317 | * the parser, it will still be managed (i.e. closed if | |
1318 | * end-of-stream is reacher, or parser close method called) | |
1319 | * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE} | |
1320 | * is enabled. | |
1321 | *<p> | |
1322 | * | |
1323 | * Note: no encoding argument is taken since it can always be | |
1324 | * auto-detected as suggested by JSON RFC. Json specification | |
1325 | * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, | |
1326 | * so auto-detection implemented only for this charsets. | |
1327 | * For other charsets use {@link #createParser(java.io.Reader)}. | |
1328 | * | |
1329 | * @param in InputStream to use for reading JSON content to parse | |
1330 | * | |
1331 | * @deprecated Since 2.2, use {@link #createParser(InputStream)} instead. | |
1332 | */ | |
1333 | @Deprecated | |
1334 | public JsonParser createJsonParser(InputStream in) throws IOException, JsonParseException { | |
1335 | return createParser(in); | |
1336 | } | |
1337 | ||
822 | 1338 | /** |
823 | 1339 | * Method for constructing parser for parsing |
824 | 1340 | * the contents accessed via specified Reader. |
831 | 1347 | * |
832 | 1348 | * @param r Reader to use for reading JSON content to parse |
833 | 1349 | * |
834 | * @since 2.1 | |
835 | */ | |
836 | public JsonParser createParser(Reader r) throws IOException, JsonParseException { | |
837 | // false -> we do NOT own Reader (did not create it) | |
838 | IOContext ctxt = _createContext(r, false); | |
839 | return _createParser(_decorate(r, ctxt), ctxt); | |
840 | } | |
841 | ||
842 | /** | |
843 | * Method for constructing parser for parsing | |
844 | * the contents of given byte array. | |
845 | * | |
846 | * @since 2.1 | |
847 | */ | |
848 | public JsonParser createParser(byte[] data) throws IOException, JsonParseException { | |
849 | IOContext ctxt = _createContext(data, true); | |
850 | if (_inputDecorator != null) { | |
851 | InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); | |
852 | if (in != null) { | |
853 | return _createParser(in, ctxt); | |
854 | } | |
855 | } | |
856 | return _createParser(data, 0, data.length, ctxt); | |
1350 | * @deprecated Since 2.2, use {@link #createParser(Reader)} instead. | |
1351 | */ | |
1352 | @Deprecated | |
1353 | public JsonParser createJsonParser(Reader r) throws IOException, JsonParseException { | |
1354 | return createParser(r); | |
1355 | } | |
1356 | ||
1357 | /** | |
1358 | * Method for constructing parser for parsing the contents of given byte array. | |
1359 | * | |
1360 | * @deprecated Since 2.2, use {@link #createParser(byte[])} instead. | |
1361 | */ | |
1362 | @Deprecated | |
1363 | public JsonParser createJsonParser(byte[] data) throws IOException, JsonParseException { | |
1364 | return createParser(data); | |
857 | 1365 | } |
858 | 1366 | |
859 | 1367 | /** |
864 | 1372 | * @param offset Offset of the first data byte within buffer |
865 | 1373 | * @param len Length of contents to parse within buffer |
866 | 1374 | * |
867 | * @since 2.1 | |
868 | */ | |
869 | public JsonParser createParser(byte[] data, int offset, int len) throws IOException, JsonParseException { | |
870 | IOContext ctxt = _createContext(data, true); | |
871 | // [JACKSON-512]: allow wrapping with InputDecorator | |
872 | if (_inputDecorator != null) { | |
873 | InputStream in = _inputDecorator.decorate(ctxt, data, offset, len); | |
874 | if (in != null) { | |
875 | return _createParser(in, ctxt); | |
876 | } | |
877 | } | |
878 | return _createParser(data, offset, len, ctxt); | |
879 | } | |
880 | ||
881 | /** | |
882 | * Method for constructing parser for parsing | |
883 | * contents of given String. | |
884 | * | |
885 | * @since 2.1 | |
886 | */ | |
887 | public JsonParser createParser(String content) throws IOException, JsonParseException { | |
888 | final int strLen = content.length(); | |
889 | // Actually, let's use this for medium-sized content, up to 64kB chunk (32kb char) | |
890 | if ((_inputDecorator != null) || (strLen > 0x8000) || !canUseCharArrays()) { | |
891 | // easier to just wrap in a Reader than extend InputDecorator; or, if content | |
892 | // is too long for us to copy it over | |
893 | return createParser(new StringReader(content)); | |
894 | } | |
895 | IOContext ctxt = _createContext(content, true); | |
896 | char[] buf = ctxt.allocTokenBuffer(strLen); | |
897 | content.getChars(0, strLen, buf, 0); | |
898 | return _createParser(buf, 0, strLen, ctxt, true); | |
899 | } | |
900 | ||
901 | /** | |
902 | * Method for constructing parser for parsing | |
903 | * contents of given char array. | |
904 | * | |
905 | * @since 2.4 | |
906 | */ | |
907 | public JsonParser createParser(char[] content) throws IOException { | |
908 | return createParser(content, 0, content.length); | |
909 | } | |
910 | ||
911 | /** | |
912 | * Method for constructing parser for parsing contents of given char array. | |
913 | * | |
914 | * @since 2.4 | |
915 | */ | |
916 | public JsonParser createParser(char[] content, int offset, int len) throws IOException { | |
917 | if (_inputDecorator != null) { // easier to just wrap in a Reader than extend InputDecorator | |
918 | return createParser(new CharArrayReader(content, offset, len)); | |
919 | } | |
920 | return _createParser(content, offset, len, _createContext(content, true), | |
921 | // important: buffer is NOT recyclable, as it's from caller | |
922 | false); | |
923 | } | |
924 | ||
925 | /** | |
926 | * Optional method for constructing parser for reading contents from specified {@link DataInput} | |
927 | * instance. | |
928 | *<p> | |
929 | * If this factory does not support {@link DataInput} as source, | |
930 | * will throw {@link UnsupportedOperationException} | |
931 | * | |
932 | * @since 2.8 | |
933 | */ | |
934 | public JsonParser createParser(DataInput in) throws IOException { | |
935 | IOContext ctxt = _createContext(in, false); | |
936 | return _createParser(_decorate(in, ctxt), ctxt); | |
937 | } | |
938 | ||
939 | /* | |
940 | /********************************************************** | |
941 | /* Parser factories, non-blocking (async) sources | |
942 | /********************************************************** | |
943 | */ | |
944 | ||
945 | /** | |
946 | * Optional method for constructing parser for non-blocking parsing | |
947 | * via {@link com.fasterxml.jackson.core.async.ByteArrayFeeder} | |
948 | * interface (accessed using {@link JsonParser#getNonBlockingInputFeeder()} | |
949 | * from constructed instance). | |
950 | *<p> | |
951 | * If this factory does not support non-blocking parsing (either at all, | |
952 | * or from byte array), | |
953 | * will throw {@link UnsupportedOperationException} | |
954 | * | |
955 | * @since 2.9 | |
956 | */ | |
957 | public JsonParser createNonBlockingByteArrayParser() throws IOException | |
958 | { | |
959 | // 17-May-2017, tatu: Need to take care not to accidentally create JSON parser | |
960 | // for non-JSON input: | |
961 | _requireJSONFactory("Non-blocking source not (yet?) support for this format (%s)"); | |
962 | IOContext ctxt = _createNonBlockingContext(null); | |
963 | ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures); | |
964 | return new NonBlockingJsonParser(ctxt, _parserFeatures, can); | |
965 | } | |
966 | ||
967 | /* | |
968 | /********************************************************** | |
969 | /* Parser factories (old ones, pre-2.2) | |
970 | /********************************************************** | |
971 | */ | |
972 | ||
973 | /** | |
974 | * Method for constructing JSON parser instance to parse | |
975 | * contents of specified file. | |
976 | *<p> | |
977 | * Encoding is auto-detected from contents according to JSON | |
978 | * specification recommended mechanism. Json specification | |
979 | * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, | |
980 | * so auto-detection implemented only for this charsets. | |
981 | * For other charsets use {@link #createParser(java.io.Reader)}. | |
982 | * | |
983 | *<p> | |
984 | * Underlying input stream (needed for reading contents) | |
985 | * will be <b>owned</b> (and managed, i.e. closed as need be) by | |
986 | * the parser, since caller has no access to it. | |
987 | * | |
988 | * @param f File that contains JSON content to parse | |
989 | * | |
990 | * @deprecated Since 2.2, use {@link #createParser(File)} instead. | |
991 | */ | |
992 | @Deprecated | |
993 | public JsonParser createJsonParser(File f) throws IOException, JsonParseException { | |
994 | return createParser(f); | |
995 | } | |
996 | ||
997 | /** | |
998 | * Method for constructing JSON parser instance to parse | |
999 | * contents of resource reference by given URL. | |
1000 | * | |
1001 | *<p> | |
1002 | * Encoding is auto-detected from contents according to JSON | |
1003 | * specification recommended mechanism. Json specification | |
1004 | * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, | |
1005 | * so auto-detection implemented only for this charsets. | |
1006 | * For other charsets use {@link #createParser(java.io.Reader)}. | |
1007 | * | |
1008 | *<p> | |
1009 | * Underlying input stream (needed for reading contents) | |
1010 | * will be <b>owned</b> (and managed, i.e. closed as need be) by | |
1011 | * the parser, since caller has no access to it. | |
1012 | * | |
1013 | * @param url URL pointing to resource that contains JSON content to parse | |
1014 | * | |
1015 | * @deprecated Since 2.2, use {@link #createParser(URL)} instead. | |
1016 | */ | |
1017 | @Deprecated | |
1018 | public JsonParser createJsonParser(URL url) throws IOException, JsonParseException { | |
1019 | return createParser(url); | |
1020 | } | |
1021 | ||
1022 | /** | |
1023 | * Method for constructing JSON parser instance to parse | |
1024 | * the contents accessed via specified input stream. | |
1025 | *<p> | |
1026 | * The input stream will <b>not be owned</b> by | |
1027 | * the parser, it will still be managed (i.e. closed if | |
1028 | * end-of-stream is reacher, or parser close method called) | |
1029 | * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE} | |
1030 | * is enabled. | |
1031 | *<p> | |
1032 | * | |
1033 | * Note: no encoding argument is taken since it can always be | |
1034 | * auto-detected as suggested by JSON RFC. Json specification | |
1035 | * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, | |
1036 | * so auto-detection implemented only for this charsets. | |
1037 | * For other charsets use {@link #createParser(java.io.Reader)}. | |
1038 | * | |
1039 | * @param in InputStream to use for reading JSON content to parse | |
1040 | * | |
1041 | * @deprecated Since 2.2, use {@link #createParser(InputStream)} instead. | |
1042 | */ | |
1043 | @Deprecated | |
1044 | public JsonParser createJsonParser(InputStream in) throws IOException, JsonParseException { | |
1045 | return createParser(in); | |
1046 | } | |
1047 | ||
1048 | /** | |
1049 | * Method for constructing parser for parsing | |
1050 | * the contents accessed via specified Reader. | |
1051 | <p> | |
1052 | * The read stream will <b>not be owned</b> by | |
1053 | * the parser, it will still be managed (i.e. closed if | |
1054 | * end-of-stream is reacher, or parser close method called) | |
1055 | * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE} | |
1056 | * is enabled. | |
1057 | * | |
1058 | * @param r Reader to use for reading JSON content to parse | |
1059 | * | |
1060 | * @deprecated Since 2.2, use {@link #createParser(Reader)} instead. | |
1061 | */ | |
1062 | @Deprecated | |
1063 | public JsonParser createJsonParser(Reader r) throws IOException, JsonParseException { | |
1064 | return createParser(r); | |
1065 | } | |
1066 | ||
1067 | /** | |
1068 | * Method for constructing parser for parsing the contents of given byte array. | |
1069 | * | |
1070 | * @deprecated Since 2.2, use {@link #createParser(byte[])} instead. | |
1071 | */ | |
1072 | @Deprecated | |
1073 | public JsonParser createJsonParser(byte[] data) throws IOException, JsonParseException { | |
1074 | return createParser(data); | |
1075 | } | |
1076 | ||
1077 | /** | |
1078 | * Method for constructing parser for parsing | |
1079 | * the contents of given byte array. | |
1080 | * | |
1081 | * @param data Buffer that contains data to parse | |
1082 | * @param offset Offset of the first data byte within buffer | |
1083 | * @param len Length of contents to parse within buffer | |
1084 | * | |
1085 | 1375 | * @deprecated Since 2.2, use {@link #createParser(byte[],int,int)} instead. |
1086 | 1376 | */ |
1087 | 1377 | @Deprecated |
1102 | 1392 | |
1103 | 1393 | /* |
1104 | 1394 | /********************************************************** |
1105 | /* Generator factories, new (as per [Issue-25] | |
1395 | /* Deprecated generator factory methods: to be removed from 3.x | |
1106 | 1396 | /********************************************************** |
1107 | 1397 | */ |
1108 | 1398 | |
1125 | 1415 | * |
1126 | 1416 | * @param out OutputStream to use for writing JSON content |
1127 | 1417 | * @param enc Character encoding to use |
1128 | * | |
1129 | * @since 2.1 | |
1130 | */ | |
1131 | public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) | |
1132 | throws IOException | |
1133 | { | |
1134 | // false -> we won't manage the stream unless explicitly directed to | |
1135 | IOContext ctxt = _createContext(out, false); | |
1136 | ctxt.setEncoding(enc); | |
1137 | if (enc == JsonEncoding.UTF8) { | |
1138 | return _createUTF8Generator(_decorate(out, ctxt), ctxt); | |
1139 | } | |
1140 | Writer w = _createWriter(out, enc, ctxt); | |
1141 | return _createGenerator(_decorate(w, ctxt), ctxt); | |
1142 | } | |
1143 | ||
1144 | /** | |
1145 | * Convenience method for constructing generator that uses default | |
1146 | * encoding of the format (UTF-8 for JSON and most other data formats). | |
1147 | *<p> | |
1148 | * Note: there are formats that use fixed encoding (like most binary data formats). | |
1149 | * | |
1150 | * @since 2.1 | |
1151 | */ | |
1152 | public JsonGenerator createGenerator(OutputStream out) throws IOException { | |
1153 | return createGenerator(out, JsonEncoding.UTF8); | |
1154 | } | |
1155 | ||
1418 | * | |
1419 | * @deprecated Since 2.2, use {@link #createGenerator(OutputStream, JsonEncoding)} instead. | |
1420 | */ | |
1421 | @Deprecated | |
1422 | public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException { | |
1423 | return createGenerator(out, enc); | |
1424 | } | |
1425 | ||
1156 | 1426 | /** |
1157 | 1427 | * Method for constructing JSON generator for writing JSON content |
1158 | 1428 | * using specified Writer. |
1163 | 1433 | * feature, |
1164 | 1434 | * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled). |
1165 | 1435 | * Using application needs to close it explicitly. |
1166 | * | |
1167 | * @since 2.1 | |
1168 | * | |
1169 | * @param w Writer to use for writing JSON content | |
1170 | */ | |
1171 | public JsonGenerator createGenerator(Writer w) throws IOException { | |
1172 | IOContext ctxt = _createContext(w, false); | |
1173 | return _createGenerator(_decorate(w, ctxt), ctxt); | |
1174 | } | |
1175 | ||
1176 | /** | |
1177 | * Method for constructing JSON generator for writing JSON content | |
1178 | * to specified file, overwriting contents it might have (or creating | |
1179 | * it if such file does not yet exist). | |
1180 | * Encoding to use must be specified, and needs to be one of available | |
1181 | * types (as per JSON specification). | |
1182 | *<p> | |
1183 | * Underlying stream <b>is owned</b> by the generator constructed, | |
1184 | * i.e. generator will handle closing of file when | |
1185 | * {@link JsonGenerator#close} is called. | |
1186 | * | |
1187 | * @param f File to write contents to | |
1188 | * @param enc Character encoding to use | |
1189 | * | |
1190 | * @since 2.1 | |
1191 | */ | |
1192 | public JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOException | |
1193 | { | |
1194 | OutputStream out = new FileOutputStream(f); | |
1195 | // true -> yes, we have to manage the stream since we created it | |
1196 | IOContext ctxt = _createContext(out, true); | |
1197 | ctxt.setEncoding(enc); | |
1198 | if (enc == JsonEncoding.UTF8) { | |
1199 | return _createUTF8Generator(_decorate(out, ctxt), ctxt); | |
1200 | } | |
1201 | Writer w = _createWriter(out, enc, ctxt); | |
1202 | return _createGenerator(_decorate(w, ctxt), ctxt); | |
1203 | } | |
1204 | ||
1205 | /** | |
1206 | * Method for constructing generator for writing content using specified | |
1207 | * {@link DataOutput} instance. | |
1208 | * | |
1209 | * @since 2.8 | |
1210 | */ | |
1211 | public JsonGenerator createGenerator(DataOutput out, JsonEncoding enc) throws IOException { | |
1212 | return createGenerator(_createDataOutputWrapper(out), enc); | |
1213 | } | |
1214 | ||
1215 | /** | |
1216 | * Convenience method for constructing generator that uses default | |
1217 | * encoding of the format (UTF-8 for JSON and most other data formats). | |
1218 | *<p> | |
1219 | * Note: there are formats that use fixed encoding (like most binary data formats). | |
1220 | * | |
1221 | * @since 2.8 | |
1222 | */ | |
1223 | public JsonGenerator createGenerator(DataOutput out) throws IOException { | |
1224 | return createGenerator(_createDataOutputWrapper(out), JsonEncoding.UTF8); | |
1225 | } | |
1226 | ||
1227 | /* | |
1228 | /********************************************************** | |
1229 | /* Generator factories, old (pre-2.2) | |
1230 | /********************************************************** | |
1231 | */ | |
1232 | ||
1233 | /** | |
1234 | * Method for constructing JSON generator for writing JSON content | |
1235 | * using specified output stream. | |
1236 | * Encoding to use must be specified, and needs to be one of available | |
1237 | * types (as per JSON specification). | |
1238 | *<p> | |
1239 | * Underlying stream <b>is NOT owned</b> by the generator constructed, | |
1240 | * so that generator will NOT close the output stream when | |
1241 | * {@link JsonGenerator#close} is called (unless auto-closing | |
1242 | * feature, | |
1243 | * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} | |
1244 | * is enabled). | |
1245 | * Using application needs to close it explicitly if this is the case. | |
1246 | *<p> | |
1247 | * Note: there are formats that use fixed encoding (like most binary data formats) | |
1248 | * and that ignore passed in encoding. | |
1249 | * | |
1250 | * @param out OutputStream to use for writing JSON content | |
1251 | * @param enc Character encoding to use | |
1252 | * | |
1253 | * @deprecated Since 2.2, use {@link #createGenerator(OutputStream, JsonEncoding)} instead. | |
1254 | */ | |
1255 | @Deprecated | |
1256 | public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException { | |
1257 | return createGenerator(out, enc); | |
1258 | } | |
1259 | ||
1260 | /** | |
1261 | * Method for constructing JSON generator for writing JSON content | |
1262 | * using specified Writer. | |
1263 | *<p> | |
1264 | * Underlying stream <b>is NOT owned</b> by the generator constructed, | |
1265 | * so that generator will NOT close the Reader when | |
1266 | * {@link JsonGenerator#close} is called (unless auto-closing | |
1267 | * feature, | |
1268 | * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled). | |
1269 | * Using application needs to close it explicitly. | |
1270 | 1436 | * |
1271 | 1437 | * @param out Writer to use for writing JSON content |
1272 | 1438 | * |
1371 | 1537 | { |
1372 | 1538 | // 13-May-2016, tatu: Need to take care not to accidentally create JSON parser for |
1373 | 1539 | // non-JSON input. |
1374 | _requireJSONFactory("InputData source not (yet?) support for this format (%s)"); | |
1540 | _requireJSONFactory("InputData source not (yet?) supported for this format (%s)"); | |
1375 | 1541 | // Also: while we can't do full bootstrapping (due to read-ahead limitations), should |
1376 | 1542 | // at least handle possible UTF-8 BOM |
1377 | 1543 | int firstByte = ByteSourceJsonBootstrapper.skipUTF8BOM(input); |
1400 | 1566 | protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException |
1401 | 1567 | { |
1402 | 1568 | WriterBasedJsonGenerator gen = new WriterBasedJsonGenerator(ctxt, |
1403 | _generatorFeatures, _objectCodec, out); | |
1569 | _generatorFeatures, _objectCodec, out, _quoteChar); | |
1570 | if (_maximumNonEscapedChar > 0) { | |
1571 | gen.setHighestNonEscapedChar(_maximumNonEscapedChar); | |
1572 | } | |
1404 | 1573 | if (_characterEscapes != null) { |
1405 | 1574 | gen.setCharacterEscapes(_characterEscapes); |
1406 | 1575 | } |
1423 | 1592 | */ |
1424 | 1593 | protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException { |
1425 | 1594 | UTF8JsonGenerator gen = new UTF8JsonGenerator(ctxt, |
1426 | _generatorFeatures, _objectCodec, out); | |
1595 | _generatorFeatures, _objectCodec, out, _quoteChar); | |
1596 | if (_maximumNonEscapedChar > 0) { | |
1597 | gen.setHighestNonEscapedChar(_maximumNonEscapedChar); | |
1598 | } | |
1427 | 1599 | if (_characterEscapes != null) { |
1428 | 1600 | gen.setCharacterEscapes(_characterEscapes); |
1429 | 1601 | } |
1514 | 1686 | } |
1515 | 1687 | return out; |
1516 | 1688 | } |
1517 | ||
1689 | ||
1518 | 1690 | /* |
1519 | 1691 | /********************************************************** |
1520 | 1692 | /* Internal factory methods, other |
1554 | 1726 | * @since 2.9.7 |
1555 | 1727 | */ |
1556 | 1728 | protected IOContext _createNonBlockingContext(Object srcRef) { |
1557 | // [jackson-core#476]: disable buffer recycling for 2.9 to avoid concurrency issues; | |
1558 | // easiest done by just constructing private "recycler": | |
1559 | BufferRecycler recycler = new BufferRecycler(); | |
1560 | return new IOContext(recycler, srcRef, false); | |
1561 | } | |
1562 | ||
1563 | /** | |
1564 | * @since 2.8 | |
1565 | */ | |
1566 | protected OutputStream _createDataOutputWrapper(DataOutput out) { | |
1567 | return new DataOutputAsStream(out); | |
1568 | } | |
1569 | ||
1570 | /** | |
1571 | * Helper methods used for constructing an optimal stream for | |
1572 | * parsers to use, when input is to be read from an URL. | |
1573 | * This helps when reading file content via URL. | |
1574 | */ | |
1575 | protected InputStream _optimizedStreamFromURL(URL url) throws IOException { | |
1576 | if ("file".equals(url.getProtocol())) { | |
1577 | /* Can not do this if the path refers | |
1578 | * to a network drive on windows. This fixes the problem; | |
1579 | * might not be needed on all platforms (NFS?), but should not | |
1580 | * matter a lot: performance penalty of extra wrapping is more | |
1581 | * relevant when accessing local file system. | |
1582 | */ | |
1583 | String host = url.getHost(); | |
1584 | if (host == null || host.length() == 0) { | |
1585 | // [core#48]: Let's try to avoid probs with URL encoded stuff | |
1586 | String path = url.getPath(); | |
1587 | if (path.indexOf('%') < 0) { | |
1588 | return new FileInputStream(url.getPath()); | |
1589 | ||
1590 | } | |
1591 | // otherwise, let's fall through and let URL decoder do its magic | |
1592 | } | |
1593 | } | |
1594 | return url.openStream(); | |
1729 | // [jackson-core#479]: allow recycling for non-blocking parser again | |
1730 | // now that access is thread-safe | |
1731 | return new IOContext(_getBufferRecycler(), srcRef, false); | |
1595 | 1732 | } |
1596 | 1733 | |
1597 | 1734 | /* |
0 | package com.fasterxml.jackson.core; | |
1 | ||
2 | import com.fasterxml.jackson.core.io.CharacterEscapes; | |
3 | import com.fasterxml.jackson.core.io.SerializedString; | |
4 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
5 | import com.fasterxml.jackson.core.json.JsonWriteFeature; | |
6 | ||
7 | /** | |
8 | * {@link com.fasterxml.jackson.core.TSFBuilder} | |
9 | * implementation for constructing vanilla {@link JsonFactory} | |
10 | * instances for reading/writing JSON encoded content. | |
11 | *<p> | |
12 | * NOTE: as of Jackson 2.x, use of JSON-specific builder is bit cumbersome | |
13 | * since {@link JsonFactory} serves dual duty of base class AND actual | |
14 | * implementation for JSON backend. This will be fixed in Jackson 3.0. | |
15 | * | |
16 | * @since 2.10 | |
17 | */ | |
18 | public class JsonFactoryBuilder extends TSFBuilder<JsonFactory, JsonFactoryBuilder> | |
19 | { | |
20 | protected CharacterEscapes _characterEscapes; | |
21 | ||
22 | protected SerializableString _rootValueSeparator; | |
23 | ||
24 | protected int _maximumNonEscapedChar; | |
25 | ||
26 | /** | |
27 | * Character used for quoting field names (if field name quoting has not | |
28 | * been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES}) | |
29 | * and JSON String values. | |
30 | */ | |
31 | protected char _quoteChar = JsonFactory.DEFAULT_QUOTE_CHAR; | |
32 | ||
33 | public JsonFactoryBuilder() { | |
34 | super(); | |
35 | _rootValueSeparator = JsonFactory.DEFAULT_ROOT_VALUE_SEPARATOR; | |
36 | _maximumNonEscapedChar = 0; | |
37 | } | |
38 | ||
39 | public JsonFactoryBuilder(JsonFactory base) { | |
40 | super(base); | |
41 | _characterEscapes = base.getCharacterEscapes(); | |
42 | _rootValueSeparator = base._rootValueSeparator; | |
43 | _maximumNonEscapedChar = base._maximumNonEscapedChar; | |
44 | } | |
45 | ||
46 | /* | |
47 | /********************************************************** | |
48 | /* Mutators | |
49 | /********************************************************** | |
50 | */ | |
51 | ||
52 | // // // JSON-parsing features | |
53 | ||
54 | @Override | |
55 | public JsonFactoryBuilder enable(JsonReadFeature f) { | |
56 | _legacyEnable(f.mappedFeature()); | |
57 | return this; | |
58 | } | |
59 | ||
60 | @Override | |
61 | public JsonFactoryBuilder enable(JsonReadFeature first, JsonReadFeature... other) { | |
62 | _legacyEnable(first.mappedFeature()); | |
63 | enable(first); | |
64 | for (JsonReadFeature f : other) { | |
65 | _legacyEnable(f.mappedFeature()); | |
66 | } | |
67 | return this; | |
68 | } | |
69 | ||
70 | @Override | |
71 | public JsonFactoryBuilder disable(JsonReadFeature f) { | |
72 | _legacyDisable(f.mappedFeature()); | |
73 | return this; | |
74 | } | |
75 | ||
76 | @Override | |
77 | public JsonFactoryBuilder disable(JsonReadFeature first, JsonReadFeature... other) { | |
78 | _legacyDisable(first.mappedFeature()); | |
79 | for (JsonReadFeature f : other) { | |
80 | _legacyEnable(f.mappedFeature()); | |
81 | } | |
82 | return this; | |
83 | } | |
84 | ||
85 | @Override | |
86 | public JsonFactoryBuilder configure(JsonReadFeature f, boolean state) { | |
87 | return state ? enable(f) : disable(f); | |
88 | } | |
89 | ||
90 | // // // JSON-generating features | |
91 | ||
92 | @Override | |
93 | public JsonFactoryBuilder enable(JsonWriteFeature f) { | |
94 | JsonGenerator.Feature old = f.mappedFeature(); | |
95 | if (old != null) { | |
96 | _legacyEnable(old); | |
97 | } | |
98 | return this; | |
99 | } | |
100 | ||
101 | @Override | |
102 | public JsonFactoryBuilder enable(JsonWriteFeature first, JsonWriteFeature... other) { | |
103 | _legacyEnable(first.mappedFeature()); | |
104 | for (JsonWriteFeature f : other) { | |
105 | _legacyEnable(f.mappedFeature()); | |
106 | } | |
107 | return this; | |
108 | } | |
109 | ||
110 | @Override | |
111 | public JsonFactoryBuilder disable(JsonWriteFeature f) { | |
112 | _legacyDisable(f.mappedFeature()); | |
113 | return this; | |
114 | } | |
115 | ||
116 | @Override | |
117 | public JsonFactoryBuilder disable(JsonWriteFeature first, JsonWriteFeature... other) { | |
118 | _legacyDisable(first.mappedFeature()); | |
119 | for (JsonWriteFeature f : other) { | |
120 | _legacyDisable(f.mappedFeature()); | |
121 | } | |
122 | return this; | |
123 | } | |
124 | ||
125 | @Override | |
126 | public JsonFactoryBuilder configure(JsonWriteFeature f, boolean state) { | |
127 | return state ? enable(f) : disable(f); | |
128 | } | |
129 | ||
130 | // // // JSON-specific helper objects, settings | |
131 | ||
132 | /** | |
133 | * Method for defining custom escapes factory uses for {@link JsonGenerator}s | |
134 | * it creates. | |
135 | */ | |
136 | public JsonFactoryBuilder characterEscapes(CharacterEscapes esc) { | |
137 | _characterEscapes = esc; | |
138 | return this; | |
139 | } | |
140 | ||
141 | /** | |
142 | * Method that allows overriding String used for separating root-level | |
143 | * JSON values (default is single space character) | |
144 | * | |
145 | * @param sep Separator to use, if any; null means that no separator is | |
146 | * automatically added | |
147 | */ | |
148 | public JsonFactoryBuilder rootValueSeparator(String sep) { | |
149 | _rootValueSeparator = (sep == null) ? null : new SerializedString(sep); | |
150 | return this; | |
151 | } | |
152 | ||
153 | /** | |
154 | * Method that allows overriding String used for separating root-level | |
155 | * JSON values (default is single space character) | |
156 | * | |
157 | * @param sep Separator to use, if any; null means that no separator is | |
158 | * automatically added | |
159 | */ | |
160 | public JsonFactoryBuilder rootValueSeparator(SerializableString sep) { | |
161 | _rootValueSeparator = sep; | |
162 | return this; | |
163 | } | |
164 | ||
165 | /** | |
166 | * Method that allows specifying threshold beyond which all characters are | |
167 | * automatically escaped (without checking possible custom escaping settings | |
168 | * a la {@link #characterEscapes}: for example, to force escaping of all non-ASCII | |
169 | * characters (set to 127), or all non-Latin-1 character (set to 255). | |
170 | * Default setting is "disabled", specified by passing value of {@code 0} (or | |
171 | * negative numbers). | |
172 | *<p> | |
173 | * NOTE! Lowest legal value (aside from marker 0) is 127: for ASCII range, other checks apply | |
174 | * and this threshold is ignored. If value between [1, 126] is specified, 127 will be | |
175 | * used instead. | |
176 | * | |
177 | * @param maxNonEscaped Highest character code that is NOT automatically escaped; if | |
178 | * positive value above 0, or 0 to indicate that no automatic escaping is applied | |
179 | * beside from what JSON specification requires (and possible custom escape settings). | |
180 | * Values between 1 and 127 are all taken to behave as if 127 is specified: that is, | |
181 | * no automatic escaping is applied in ASCII range. | |
182 | */ | |
183 | public JsonFactoryBuilder highestNonEscapedChar(int maxNonEscaped) { | |
184 | _maximumNonEscapedChar = (maxNonEscaped <= 0) ? 0 : Math.max(127, maxNonEscaped); | |
185 | return this; | |
186 | } | |
187 | ||
188 | /** | |
189 | * Method that allows specifying an alternate | |
190 | * character used for quoting field names (if field name quoting has not | |
191 | * been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES}) | |
192 | * and JSON String values. | |
193 | *<p> | |
194 | * Default value is double-quote ({@code "}); typical alternative is | |
195 | * single-quote/apostrophe ({@code '}). | |
196 | * | |
197 | * @param ch Character to use for quoting field names and JSON String values. | |
198 | */ | |
199 | public JsonFactoryBuilder quoteChar(char ch) { | |
200 | // 12-Aug-2019, tatu: Due to implementation details, escaping characters beyond | |
201 | // 7-bit ASCII set has deep overhead so let's limit set. If we absolutely | |
202 | // must it is possible of course, but leads to problems combining with | |
203 | // custom escaping aspects. | |
204 | if (ch > 0x7F) { | |
205 | throw new IllegalArgumentException("Can only use Unicode characters up to 0x7F as quote characters"); | |
206 | } | |
207 | _quoteChar = ch; | |
208 | return this; | |
209 | } | |
210 | ||
211 | // // // Accessors for JSON-specific settings | |
212 | ||
213 | public CharacterEscapes characterEscapes() { return _characterEscapes; } | |
214 | public SerializableString rootValueSeparator() { return _rootValueSeparator; } | |
215 | ||
216 | public int highestNonEscapedChar() { return _maximumNonEscapedChar; } | |
217 | ||
218 | public char quoteChar() { return _quoteChar; } | |
219 | ||
220 | @Override | |
221 | public JsonFactory build() { | |
222 | // 28-Dec-2017, tatu: No special settings beyond base class ones, so: | |
223 | return new JsonFactory(this); | |
224 | } | |
225 | } |
33 | 33 | */ |
34 | 34 | public enum Feature { |
35 | 35 | // // Low-level I/O / content features |
36 | ||
36 | ||
37 | 37 | /** |
38 | 38 | * Feature that determines whether generator will automatically |
39 | 39 | * close underlying output target that is NOT owned by the |
84 | 84 | * occurs when used straight from Javascript. |
85 | 85 | *<p> |
86 | 86 | * Feature is enabled by default (since it is required by JSON specification). |
87 | * | |
88 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#QUOTE_FIELD_NAMES} instead | |
87 | 89 | */ |
90 | @Deprecated | |
88 | 91 | QUOTE_FIELD_NAMES(true), |
89 | 92 | |
90 | 93 | /** |
98 | 101 | * output. |
99 | 102 | *<p> |
100 | 103 | * Feature is enabled by default. |
104 | * | |
105 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NAN_AS_STRINGS} instead | |
101 | 106 | */ |
107 | @Deprecated | |
102 | 108 | QUOTE_NON_NUMERIC_NUMBERS(true), |
103 | 109 | |
104 | /** | |
105 | * Feature that forces all Java numbers to be written as JSON strings. | |
106 | * Default state is 'false', meaning that Java numbers are to | |
107 | * be serialized using basic numeric serialization (as JSON | |
108 | * numbers, integral or floating point). If enabled, all such | |
109 | * numeric values are instead written out as JSON Strings. | |
110 | *<p> | |
111 | * One use case is to avoid problems with Javascript limitations: | |
112 | * since Javascript standard specifies that all number handling | |
113 | * should be done using 64-bit IEEE 754 floating point values, | |
114 | * result being that some 64-bit integer values can not be | |
115 | * accurately represent (as mantissa is only 51 bit wide). | |
116 | *<p> | |
117 | * Feature is disabled by default. | |
118 | */ | |
119 | WRITE_NUMBERS_AS_STRINGS(false), | |
120 | ||
121 | /** | |
122 | * Feature that determines whether {@link java.math.BigDecimal} entries are | |
123 | * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent | |
124 | * values to be written using scientific notation. | |
125 | *<p> | |
126 | * Feature is disabled by default, so default output mode is used; this generally | |
127 | * depends on how {@link BigDecimal} has been created. | |
128 | * | |
129 | * @since 2.3 | |
130 | */ | |
131 | WRITE_BIGDECIMAL_AS_PLAIN(false), | |
132 | ||
110 | // // Character escaping features | |
111 | ||
133 | 112 | /** |
134 | 113 | * Feature that specifies that all characters beyond 7-bit ASCII |
135 | 114 | * range (i.e. code points of 128 and above) need to be output |
144 | 123 | * Put another way, effects of this feature are data-format specific. |
145 | 124 | *<p> |
146 | 125 | * Feature is disabled by default. |
126 | * | |
127 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#ESCAPE_NON_ASCII} instead | |
147 | 128 | */ |
129 | @Deprecated | |
148 | 130 | ESCAPE_NON_ASCII(false), |
149 | 131 | |
150 | // 23-Nov-2015, tatu: for [core#223], if and when it gets implemented | |
132 | // // Datatype coercion features | |
133 | ||
151 | 134 | /** |
152 | * Feature that specifies handling of UTF-8 content that contains | |
153 | * characters beyond BMP (Basic Multilingual Plane), which are | |
154 | * represented in UCS-2 (Java internal character encoding) as two | |
155 | * "surrogate" characters. If feature is enabled, these surrogate | |
156 | * pairs are separately escaped using backslash escapes; if disabled, | |
157 | * native output (4-byte UTF-8 sequence, or, with char-backed output | |
158 | * targets, writing of surrogates as is which is typically converted | |
159 | * by {@link java.io.Writer} into 4-byte UTF-8 sequence eventually) | |
160 | * is used. | |
135 | * Feature that forces all Java numbers to be written as Strings, | |
136 | * even if the underlying data format has non-textual representation | |
137 | * (which is the case for JSON as well as all binary formats). | |
138 | * Default state is 'false', meaning that Java numbers are to | |
139 | * be serialized using basic numeric serialization (as JSON | |
140 | * numbers, integral or floating point, for example). | |
141 | * If enabled, all such numeric values are instead written out as | |
142 | * textual values (which for JSON means quoted in double-quotes). | |
161 | 143 | *<p> |
162 | * Note that the original JSON specification suggests use of escaping; | |
163 | * but that this is not correct from standard UTF-8 handling perspective. | |
164 | * Because of two competing goals, this feature was added to allow either | |
165 | * behavior to be used, but defaulting to UTF-8 specification compliant | |
166 | * mode. | |
144 | * One use case is to avoid problems with Javascript limitations: | |
145 | * since Javascript standard specifies that all number handling | |
146 | * should be done using 64-bit IEEE 754 floating point values, | |
147 | * result being that some 64-bit integer values can not be | |
148 | * accurately represent (as mantissa is only 51 bit wide). | |
167 | 149 | *<p> |
168 | 150 | * Feature is disabled by default. |
169 | 151 | * |
170 | * @since Xxx | |
152 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NUMBERS_AS_STRINGS} instead | |
171 | 153 | */ |
172 | // ESCAPE_UTF8_SURROGATES(false), | |
173 | ||
154 | @Deprecated | |
155 | WRITE_NUMBERS_AS_STRINGS(false), | |
156 | ||
157 | /** | |
158 | * Feature that determines whether {@link java.math.BigDecimal} entries are | |
159 | * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent | |
160 | * values to be written using scientific notation. | |
161 | *<p> | |
162 | * NOTE: only affects generators that serialize {@link java.math.BigDecimal}s | |
163 | * using textual representation (textual formats but potentially some binary | |
164 | * formats). | |
165 | *<p> | |
166 | * Feature is disabled by default, so default output mode is used; this generally | |
167 | * depends on how {@link BigDecimal} has been created. | |
168 | * | |
169 | * @since 2.3 | |
170 | */ | |
171 | WRITE_BIGDECIMAL_AS_PLAIN(false), | |
172 | ||
174 | 173 | // // Schema/Validity support features |
175 | 174 | |
176 | 175 | /** |
329 | 328 | public abstract boolean isEnabled(Feature f); |
330 | 329 | |
331 | 330 | /** |
331 | * @since 2.10 | |
332 | */ | |
333 | public boolean isEnabled(StreamWriteFeature f) { | |
334 | return isEnabled(f.mappedFeature()); | |
335 | } | |
336 | ||
337 | /** | |
332 | 338 | * Bulk access method for getting state of all standard (non-dataformat-specific) |
333 | 339 | * {@link JsonGenerator.Feature}s. |
334 | 340 | * |
400 | 406 | * @since 2.6 |
401 | 407 | */ |
402 | 408 | public JsonGenerator overrideFormatFeatures(int values, int mask) { |
403 | throw new IllegalArgumentException("No FormatFeatures defined for generator of type "+getClass().getName()); | |
404 | /* | |
405 | int oldState = getFeatureMask(); | |
406 | int newState = (oldState & ~mask) | (values & mask); | |
407 | return setFeatureMask(newState); | |
408 | */ | |
409 | // 08-Oct-2018, tatu: For 2.10 we actually do get `JsonWriteFeature`s, although they | |
410 | // are (for 2.x only, not for 3.x) mapper to legacy settings. So do not freak out: | |
411 | // throw new IllegalArgumentException("No FormatFeatures defined for generator of type "+getClass().getName()); | |
412 | return this; | |
409 | 413 | } |
410 | 414 | |
411 | 415 | /* |
516 | 520 | * simply return 0. |
517 | 521 | * |
518 | 522 | * @return Currently active limitation for highest non-escaped character, |
519 | * if defined; or -1 to indicate no additional escaping is performed. | |
523 | * if defined; or 0 to indicate no additional escaping is performed. | |
520 | 524 | */ |
521 | 525 | public int getHighestEscapedChar() { return 0; } |
522 | 526 | |
730 | 734 | */ |
731 | 735 | public abstract void writeStartArray() throws IOException; |
732 | 736 | |
737 | // TODO: deprecate in 2.11 (remove from 3.0) | |
733 | 738 | /** |
734 | 739 | * Method for writing start marker of an Array value, similar |
735 | 740 | * to {@link #writeStartArray()}, but also specifying how many |
748 | 753 | public void writeStartArray(int size) throws IOException { |
749 | 754 | writeStartArray(); |
750 | 755 | } |
751 | ||
756 | ||
757 | /** | |
758 | * @since 2.10 | |
759 | */ | |
760 | public void writeStartArray(Object forValue) throws IOException { | |
761 | writeStartArray(); | |
762 | setCurrentValue(forValue); | |
763 | } | |
764 | ||
765 | /** | |
766 | * @since 2.10 | |
767 | */ | |
768 | public void writeStartArray(Object forValue, int size) throws IOException { | |
769 | writeStartArray(size); | |
770 | setCurrentValue(forValue); | |
771 | } | |
772 | ||
752 | 773 | /** |
753 | 774 | * Method for writing closing marker of a JSON Array value |
754 | 775 | * (character ']'; plus possible white space decoration |
760 | 781 | public abstract void writeEndArray() throws IOException; |
761 | 782 | |
762 | 783 | /** |
763 | * Method for writing starting marker of a JSON Object value | |
784 | * Method for writing starting marker of an Object value | |
764 | 785 | * (character '{'; plus possible white space decoration |
765 | 786 | * if pretty-printing is enabled). |
766 | 787 | *<p> |
771 | 792 | public abstract void writeStartObject() throws IOException; |
772 | 793 | |
773 | 794 | /** |
774 | * Method for writing starting marker of a JSON Object value | |
775 | * (character '{'; plus possible white space decoration | |
776 | * if pretty-printing is enabled), to represent Java given | |
777 | * as the argument. Argument is offered as metadata, but more | |
795 | * Method for writing starting marker of an Object value | |
796 | * to represent the given Java Object value. | |
797 | * Argument is offered as metadata, but more | |
778 | 798 | * importantly it should be assigned as the "current value" |
779 | 799 | * for the Object content that gets constructed and initialized. |
780 | 800 | *<p> |
782 | 802 | * are allowed: meaning everywhere except for when |
783 | 803 | * a field name is expected. |
784 | 804 | * |
785 | * @since 2.8. | |
805 | * @since 2.8 | |
786 | 806 | */ |
787 | 807 | public void writeStartObject(Object forValue) throws IOException |
788 | 808 | { |
791 | 811 | } |
792 | 812 | |
793 | 813 | /** |
794 | * Method for writing closing marker of a JSON Object value | |
814 | * Method for writing starting marker of an Object value | |
815 | * to represent the given Java Object value. | |
816 | * Argument is offered as metadata, but more | |
817 | * importantly it should be assigned as the "current value" | |
818 | * for the Object content that gets constructed and initialized. | |
819 | * In addition, caller knows number of key/value pairs ("properties") | |
820 | * that will get written for the Object value: this is relevant for | |
821 | * some format backends (but not, as an example, for JSON). | |
822 | *<p> | |
823 | * Object values can be written in any context where values | |
824 | * are allowed: meaning everywhere except for when | |
825 | * a field name is expected. | |
826 | * | |
827 | * @since 2.10 | |
828 | */ | |
829 | public void writeStartObject(Object forValue, int size) throws IOException | |
830 | { | |
831 | writeStartObject(); | |
832 | setCurrentValue(forValue); | |
833 | } | |
834 | ||
835 | /** | |
836 | * Method for writing closing marker of an Object value | |
795 | 837 | * (character '}'; plus possible white space decoration |
796 | 838 | * if pretty-printing is enabled). |
797 | 839 | *<p> |
1090 | 1132 | * |
1091 | 1133 | * @since 2.1 |
1092 | 1134 | */ |
1135 | // public abstract void writeRaw(SerializableString raw) throws IOException; | |
1093 | 1136 | public void writeRaw(SerializableString raw) throws IOException { |
1094 | 1137 | writeRaw(raw.getValue()); |
1095 | 1138 | } |
1695 | 1738 | |
1696 | 1739 | /** |
1697 | 1740 | * Convenience method for outputting a field entry ("member") |
1698 | * (that will contain a JSON Object value), and the START_OBJECT marker. | |
1741 | * (that will contain an Object value), and the START_OBJECT marker. | |
1699 | 1742 | * Equivalent to: |
1700 | 1743 | *<pre> |
1701 | 1744 | * writeFieldName(fieldName); |
1755 | 1798 | public void copyCurrentEvent(JsonParser p) throws IOException |
1756 | 1799 | { |
1757 | 1800 | JsonToken t = p.currentToken(); |
1758 | // sanity check; what to do? | |
1759 | if (t == null) { | |
1760 | _reportError("No current event to copy"); | |
1761 | } | |
1762 | switch (t.id()) { | |
1801 | final int token = (t == null) ? ID_NOT_AVAILABLE : t.id(); | |
1802 | switch (token) { | |
1763 | 1803 | case ID_NOT_AVAILABLE: |
1764 | 1804 | _reportError("No current event to copy"); |
1765 | 1805 | break; // never gets here |
1822 | 1862 | writeObject(p.getEmbeddedObject()); |
1823 | 1863 | break; |
1824 | 1864 | default: |
1825 | _throwInternal(); | |
1865 | throw new IllegalStateException("Internal error: unknown current token, "+t); | |
1826 | 1866 | } |
1827 | 1867 | } |
1828 | 1868 | |
1859 | 1899 | public void copyCurrentStructure(JsonParser p) throws IOException |
1860 | 1900 | { |
1861 | 1901 | JsonToken t = p.currentToken(); |
1862 | if (t == null) { | |
1863 | _reportError("No current event to copy"); | |
1864 | } | |
1865 | 1902 | // Let's handle field-name separately first |
1866 | int id = t.id(); | |
1903 | int id = (t == null) ? ID_NOT_AVAILABLE : t.id(); | |
1867 | 1904 | if (id == ID_FIELD_NAME) { |
1868 | 1905 | writeFieldName(p.getCurrentName()); |
1869 | 1906 | t = p.nextToken(); |
1870 | id = t.id(); | |
1907 | id = (t == null) ? ID_NOT_AVAILABLE : t.id(); | |
1871 | 1908 | // fall-through to copy the associated value |
1872 | 1909 | } |
1873 | 1910 | switch (id) { |
1874 | 1911 | case ID_START_OBJECT: |
1875 | 1912 | writeStartObject(); |
1876 | while (p.nextToken() != JsonToken.END_OBJECT) { | |
1877 | copyCurrentStructure(p); | |
1878 | } | |
1879 | writeEndObject(); | |
1880 | break; | |
1913 | _copyCurrentContents(p); | |
1914 | return; | |
1881 | 1915 | case ID_START_ARRAY: |
1882 | 1916 | writeStartArray(); |
1883 | while (p.nextToken() != JsonToken.END_ARRAY) { | |
1884 | copyCurrentStructure(p); | |
1885 | } | |
1886 | writeEndArray(); | |
1887 | break; | |
1917 | _copyCurrentContents(p); | |
1918 | return; | |
1919 | ||
1888 | 1920 | default: |
1889 | 1921 | copyCurrentEvent(p); |
1922 | } | |
1923 | } | |
1924 | ||
1925 | /** | |
1926 | * @since 2.10 | |
1927 | */ | |
1928 | protected void _copyCurrentContents(JsonParser p) throws IOException | |
1929 | { | |
1930 | int depth = 1; | |
1931 | JsonToken t; | |
1932 | ||
1933 | // Mostly copied from `copyCurrentEvent()`, but with added nesting counts | |
1934 | while ((t = p.nextToken()) != null) { | |
1935 | switch (t.id()) { | |
1936 | case ID_FIELD_NAME: | |
1937 | writeFieldName(p.getCurrentName()); | |
1938 | break; | |
1939 | ||
1940 | case ID_START_ARRAY: | |
1941 | writeStartArray(); | |
1942 | ++depth; | |
1943 | break; | |
1944 | ||
1945 | case ID_START_OBJECT: | |
1946 | writeStartObject(); | |
1947 | ++depth; | |
1948 | break; | |
1949 | ||
1950 | case ID_END_ARRAY: | |
1951 | writeEndArray(); | |
1952 | if (--depth == 0) { | |
1953 | return; | |
1954 | } | |
1955 | break; | |
1956 | case ID_END_OBJECT: | |
1957 | writeEndObject(); | |
1958 | if (--depth == 0) { | |
1959 | return; | |
1960 | } | |
1961 | break; | |
1962 | ||
1963 | case ID_STRING: | |
1964 | if (p.hasTextCharacters()) { | |
1965 | writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength()); | |
1966 | } else { | |
1967 | writeString(p.getText()); | |
1968 | } | |
1969 | break; | |
1970 | case ID_NUMBER_INT: | |
1971 | { | |
1972 | NumberType n = p.getNumberType(); | |
1973 | if (n == NumberType.INT) { | |
1974 | writeNumber(p.getIntValue()); | |
1975 | } else if (n == NumberType.BIG_INTEGER) { | |
1976 | writeNumber(p.getBigIntegerValue()); | |
1977 | } else { | |
1978 | writeNumber(p.getLongValue()); | |
1979 | } | |
1980 | break; | |
1981 | } | |
1982 | case ID_NUMBER_FLOAT: | |
1983 | { | |
1984 | NumberType n = p.getNumberType(); | |
1985 | if (n == NumberType.BIG_DECIMAL) { | |
1986 | writeNumber(p.getDecimalValue()); | |
1987 | } else if (n == NumberType.FLOAT) { | |
1988 | writeNumber(p.getFloatValue()); | |
1989 | } else { | |
1990 | writeNumber(p.getDoubleValue()); | |
1991 | } | |
1992 | break; | |
1993 | } | |
1994 | case ID_TRUE: | |
1995 | writeBoolean(true); | |
1996 | break; | |
1997 | case ID_FALSE: | |
1998 | writeBoolean(false); | |
1999 | break; | |
2000 | case ID_NULL: | |
2001 | writeNull(); | |
2002 | break; | |
2003 | case ID_EMBEDDED_OBJECT: | |
2004 | writeObject(p.getEmbeddedObject()); | |
2005 | break; | |
2006 | default: | |
2007 | throw new IllegalStateException("Internal error: unknown current token, "+t); | |
2008 | } | |
1890 | 2009 | } |
1891 | 2010 | } |
1892 | 2011 |
4 | 4 | |
5 | 5 | package com.fasterxml.jackson.core; |
6 | 6 | |
7 | import com.fasterxml.jackson.core.exc.StreamReadException; | |
7 | 8 | import com.fasterxml.jackson.core.util.RequestPayload; |
8 | 9 | |
9 | 10 | /** |
11 | 12 | * (content that does not conform to JSON syntax as per specification) |
12 | 13 | * is encountered. |
13 | 14 | */ |
14 | public class JsonParseException extends JsonProcessingException { | |
15 | public class JsonParseException extends StreamReadException | |
16 | { | |
15 | 17 | private static final long serialVersionUID = 2L; // 2.7 |
16 | ||
17 | // transient since 2.7.4 | |
18 | protected transient JsonParser _processor; | |
19 | ||
20 | /** | |
21 | * Optional payload that can be assigned to pass along for error reporting | |
22 | * or handling purposes. Core streaming parser implementations DO NOT | |
23 | * initialize this; it is up to using applications and frameworks to | |
24 | * populate it. | |
25 | * | |
26 | * @since 2.8 | |
27 | */ | |
28 | protected RequestPayload _requestPayload; | |
29 | 18 | |
30 | 19 | @Deprecated // since 2.7 |
31 | 20 | public JsonParseException(String msg, JsonLocation loc) { |
32 | super(msg, loc); | |
21 | super(msg, loc, null); | |
33 | 22 | } |
34 | 23 | |
35 | 24 | @Deprecated // since 2.7 |
45 | 34 | * @since 2.7 |
46 | 35 | */ |
47 | 36 | public JsonParseException(JsonParser p, String msg) { |
48 | super(msg, (p == null) ? null : p.getCurrentLocation()); | |
49 | _processor = p; | |
37 | super(p, msg); | |
50 | 38 | } |
51 | 39 | |
52 | 40 | /** |
53 | 41 | * @since 2.7 |
54 | 42 | */ |
55 | 43 | public JsonParseException(JsonParser p, String msg, Throwable root) { |
56 | super(msg, (p == null) ? null : p.getCurrentLocation(), root); | |
57 | _processor = p; | |
44 | super(p, msg, root); | |
58 | 45 | } |
59 | 46 | |
60 | 47 | /** |
61 | 48 | * @since 2.7 |
62 | 49 | */ |
63 | 50 | public JsonParseException(JsonParser p, String msg, JsonLocation loc) { |
64 | super(msg, loc); | |
65 | _processor = p; | |
51 | super(p, msg, loc); | |
66 | 52 | } |
67 | 53 | |
68 | 54 | /** |
70 | 56 | */ |
71 | 57 | public JsonParseException(JsonParser p, String msg, JsonLocation loc, Throwable root) { |
72 | 58 | super(msg, loc, root); |
73 | _processor = p; | |
74 | 59 | } |
75 | 60 | |
76 | 61 | /** |
81 | 66 | * |
82 | 67 | * @since 2.7 |
83 | 68 | */ |
69 | @Override | |
84 | 70 | public JsonParseException withParser(JsonParser p) { |
85 | 71 | _processor = p; |
86 | 72 | return this; |
94 | 80 | * |
95 | 81 | * @since 2.8 |
96 | 82 | */ |
83 | @Override | |
97 | 84 | public JsonParseException withRequestPayload(RequestPayload p) { |
98 | 85 | _requestPayload = p; |
99 | 86 | return this; |
100 | 87 | } |
101 | ||
88 | ||
89 | // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) | |
102 | 90 | @Override |
103 | 91 | public JsonParser getProcessor() { |
104 | return _processor; | |
92 | return super.getProcessor(); | |
105 | 93 | } |
106 | 94 | |
107 | /** | |
108 | * Method that may be called to find payload that was being parsed, if | |
109 | * one was specified for parser that threw this Exception. | |
110 | * | |
111 | * @return request body, if payload was specified; `null` otherwise | |
112 | * | |
113 | * @since 2.8 | |
114 | */ | |
95 | // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) | |
96 | @Override | |
115 | 97 | public RequestPayload getRequestPayload() { |
116 | return _requestPayload; | |
98 | return super.getRequestPayload(); | |
117 | 99 | } |
118 | 100 | |
119 | /** | |
120 | * The method returns the String representation of the request payload if | |
121 | * one was specified for parser that threw this Exception. | |
122 | * | |
123 | * @return request body as String, if payload was specified; `null` otherwise | |
124 | * | |
125 | * @since 2.8 | |
126 | */ | |
101 | // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) | |
102 | @Override | |
127 | 103 | public String getRequestPayloadAsString() { |
128 | return (_requestPayload != null) ? _requestPayload.toString() : null; | |
104 | return super.getRequestPayloadAsString(); | |
129 | 105 | } |
130 | 106 | |
131 | /** | |
132 | * Overriding the getMessage() to include the request body | |
133 | */ | |
107 | // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) | |
134 | 108 | @Override |
135 | 109 | public String getMessage() { |
136 | String msg = super.getMessage(); | |
137 | if (_requestPayload != null) { | |
138 | msg += "\nRequest payload : " + _requestPayload.toString(); | |
139 | } | |
140 | return msg; | |
110 | return super.getMessage(); | |
141 | 111 | } |
142 | 112 | } |
42 | 42 | * Enumeration that defines all on/off features for parsers. |
43 | 43 | */ |
44 | 44 | public enum Feature { |
45 | ||
46 | 45 | // // // Low-level I/O handling features: |
47 | ||
46 | ||
48 | 47 | /** |
49 | 48 | * Feature that determines whether parser will automatically |
50 | 49 | * close underlying input source that is NOT owned by the |
72 | 71 | * this is extensively used. As such, feature is |
73 | 72 | * <b>disabled by default</b> for parsers and must be |
74 | 73 | * explicitly enabled. |
74 | *<p> | |
75 | * NOTE: while not technically deprecated, since 2.10 recommended to use | |
76 | * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_JAVA_COMMENTS} instead. | |
75 | 77 | */ |
76 | 78 | ALLOW_COMMENTS(false), |
77 | 79 | |
86 | 88 | * this is a non-standard feature. As such, feature is |
87 | 89 | * <b>disabled by default</b> for parsers and must be |
88 | 90 | * explicitly enabled. |
91 | *<p> | |
92 | * NOTE: while not technically deprecated, since 2.10 recommended to use | |
93 | * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_YAML_COMMENTS} instead. | |
89 | 94 | */ |
90 | 95 | ALLOW_YAML_COMMENTS(false), |
91 | ||
96 | ||
92 | 97 | /** |
93 | 98 | * Feature that determines whether parser will allow use |
94 | 99 | * of unquoted field names (which is allowed by Javascript, |
97 | 102 | * Since JSON specification requires use of double quotes for |
98 | 103 | * field names, |
99 | 104 | * this is a non-standard feature, and as such disabled by default. |
105 | *<p> | |
106 | * NOTE: while not technically deprecated, since 2.10 recommended to use | |
107 | * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_UNQUOTED_FIELD_NAMES} instead. | |
100 | 108 | */ |
101 | 109 | ALLOW_UNQUOTED_FIELD_NAMES(false), |
102 | 110 | |
110 | 118 | * Since JSON specification requires use of double quotes for |
111 | 119 | * field names, |
112 | 120 | * this is a non-standard feature, and as such disabled by default. |
121 | *<p> | |
122 | * NOTE: while not technically deprecated, since 2.10 recommended to use | |
123 | * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_SINGLE_QUOTES} instead. | |
113 | 124 | */ |
114 | 125 | ALLOW_SINGLE_QUOTES(false), |
115 | 126 | |
123 | 134 | *<p> |
124 | 135 | * Since JSON specification requires quoting for all control characters, |
125 | 136 | * this is a non-standard feature, and as such disabled by default. |
137 | * | |
138 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_UNESCAPED_CONTROL_CHARS} instead | |
126 | 139 | */ |
140 | @Deprecated | |
127 | 141 | ALLOW_UNQUOTED_CONTROL_CHARS(false), |
128 | 142 | |
129 | 143 | /** |
134 | 148 | *<p> |
135 | 149 | * Since JSON specification requires quoting for all control characters, |
136 | 150 | * this is a non-standard feature, and as such disabled by default. |
151 | * | |
152 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER} instead | |
137 | 153 | */ |
154 | @Deprecated | |
138 | 155 | ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false), |
139 | 156 | |
140 | 157 | /** |
146 | 163 | *<p> |
147 | 164 | * Since JSON specification does not allow leading zeroes, |
148 | 165 | * this is a non-standard feature, and as such disabled by default. |
166 | * | |
167 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_LEADING_ZEROS_FOR_NUMBERS} instead | |
149 | 168 | */ |
169 | @Deprecated | |
150 | 170 | ALLOW_NUMERIC_LEADING_ZEROS(false), |
151 | 171 | |
152 | 172 | /** |
166 | 186 | *<p> |
167 | 187 | * Since JSON specification does not allow use of such values, |
168 | 188 | * this is a non-standard feature, and as such disabled by default. |
169 | */ | |
189 | * | |
190 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_NON_NUMERIC_NUMBERS} instead | |
191 | */ | |
192 | @Deprecated | |
170 | 193 | ALLOW_NON_NUMERIC_NUMBERS(false), |
171 | 194 | |
172 | 195 | /** |
184 | 207 | * feature and is disabled by default. |
185 | 208 | * |
186 | 209 | * @since 2.8 |
210 | * | |
211 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_MISSING_VALUES} instead | |
187 | 212 | */ |
213 | @Deprecated | |
188 | 214 | ALLOW_MISSING_VALUES(false), |
189 | 215 | |
190 | 216 | /** |
207 | 233 | * feature, and as such disabled by default. |
208 | 234 | * |
209 | 235 | * @since 2.9 |
236 | * | |
237 | * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_TRAILING_COMMA} instead | |
210 | 238 | */ |
239 | @Deprecated | |
211 | 240 | ALLOW_TRAILING_COMMA(false), |
212 | 241 | |
213 | 242 | // // // Validity checks |
276 | 305 | * @since 2.9 |
277 | 306 | */ |
278 | 307 | INCLUDE_SOURCE_IN_LOCATION(true), |
279 | ||
308 | ||
280 | 309 | ; |
281 | 310 | |
282 | 311 | /** |
684 | 713 | public boolean isEnabled(Feature f) { return f.enabledIn(_features); } |
685 | 714 | |
686 | 715 | /** |
716 | * Method for checking whether specified {@link Feature} is enabled. | |
717 | * | |
718 | * @since 2.10 | |
719 | */ | |
720 | public boolean isEnabled(StreamReadFeature f) { return f.mappedFeature().enabledIn(_features); } | |
721 | ||
722 | /** | |
687 | 723 | * Bulk access method for getting state of all standard {@link Feature}s. |
688 | 724 | * |
689 | 725 | * @return Bit mask that defines current states of all standard {@link Feature}s. |
753 | 789 | * @since 2.6 |
754 | 790 | */ |
755 | 791 | public JsonParser overrideFormatFeatures(int values, int mask) { |
756 | throw new IllegalArgumentException("No FormatFeatures defined for parser of type "+getClass().getName()); | |
757 | /* | |
758 | _formatFeatures = (_formatFeatures & ~mask) | (values & mask); | |
759 | */ | |
792 | // 08-Oct-2018, tatu: For 2.10 we actually do get `JsonReadFeature`s, although they | |
793 | // are (for 2.x only, not for 3.x) mapper to legacy settings. So do not freak out: | |
794 | // throw new IllegalArgumentException("No FormatFeatures defined for parser of type "+getClass().getName()); | |
795 | return this; | |
760 | 796 | } |
761 | 797 | |
762 | 798 | /* |
1763 | 1799 | * Method for reading sequence of Objects from parser stream, |
1764 | 1800 | * all with same specified value type. |
1765 | 1801 | */ |
1766 | public <T> Iterator<T> readValuesAs(TypeReference<?> valueTypeRef) throws IOException { | |
1802 | public <T> Iterator<T> readValuesAs(TypeReference<T> valueTypeRef) throws IOException { | |
1767 | 1803 | return _codec().readValues(this, valueTypeRef); |
1768 | 1804 | } |
1769 | 1805 |
137 | 137 | public static JsonPointer valueOf(String input) { return compile(input); } |
138 | 138 | |
139 | 139 | /** |
140 | * Accessor for an "empty" expression, that is, one you can get by | |
141 | * calling {@link #compile} with "" (empty String). | |
142 | *<p> | |
143 | * NOTE: this is different from expression for {@code "/"} which would | |
144 | * instead match Object node property with empty String ("") as name. | |
145 | * | |
146 | * @since 2.10 | |
147 | */ | |
148 | public static JsonPointer empty() { return EMPTY; } | |
149 | ||
150 | /** | |
140 | 151 | * Factory method that will construct a pointer instance that describes |
141 | 152 | * path to location given {@link JsonStreamContext} points to. |
142 | 153 | * |
247 | 258 | public boolean matches() { return _nextSegment == null; } |
248 | 259 | public String getMatchingProperty() { return _matchingPropertyName; } |
249 | 260 | public int getMatchingIndex() { return _matchingElementIndex; } |
261 | ||
262 | /** | |
263 | * @return True if the root selector matches property name (that is, could | |
264 | * match field value of JSON Object node) | |
265 | */ | |
250 | 266 | public boolean mayMatchProperty() { return _matchingPropertyName != null; } |
267 | ||
268 | /** | |
269 | * @return True if the root selector matches element index (that is, could | |
270 | * match an element of JSON Array node) | |
271 | */ | |
251 | 272 | public boolean mayMatchElement() { return _matchingElementIndex >= 0; } |
252 | 273 | |
253 | 274 | /** |
18 | 18 | protected JsonLocation _location; |
19 | 19 | |
20 | 20 | protected JsonProcessingException(String msg, JsonLocation loc, Throwable rootCause) { |
21 | /* Argh. IOException(Throwable,String) is only available starting | |
22 | * with JDK 1.6... | |
23 | */ | |
24 | 21 | super(msg); |
25 | 22 | if (rootCause != null) { |
26 | 23 | initCause(rootCause); |
55 | 55 | * and specifically needs to be used if the root type is a |
56 | 56 | * parameterized (generic) container type. |
57 | 57 | */ |
58 | public abstract <T> T readValue(JsonParser p, TypeReference<?> valueTypeRef) | |
58 | public abstract <T> T readValue(JsonParser p, TypeReference<T> valueTypeRef) | |
59 | 59 | throws IOException; |
60 | 60 | |
61 | 61 | /** |
78 | 78 | * Method for reading sequence of Objects from parser stream, |
79 | 79 | * all with same specified value type. |
80 | 80 | */ |
81 | public abstract <T> Iterator<T> readValues(JsonParser p, TypeReference<?> valueTypeRef) | |
81 | public abstract <T> Iterator<T> readValues(JsonParser p, TypeReference<T> valueTypeRef) | |
82 | 82 | throws IOException; |
83 | 83 | |
84 | 84 | /** |
30 | 30 | |
31 | 31 | /** |
32 | 32 | * Returns length of the (unquoted) String as characters. |
33 | * Functionally equvalent to: | |
33 | * Functionally equivalent to: | |
34 | 34 | *<pre> |
35 | 35 | * getValue().length(); |
36 | 36 | *</pre> |
0 | package com.fasterxml.jackson.core; | |
1 | ||
2 | import java.io.InputStream; | |
3 | import java.io.Reader; | |
4 | ||
5 | /** | |
6 | * Token reader (parser) features not-specific to any particular format backend. | |
7 | * Eventual replacement for non-JSON-specific {@link com.fasterxml.jackson.core.JsonParser.Feature}s. | |
8 | * | |
9 | * @since 2.10 | |
10 | */ | |
11 | public enum StreamReadFeature | |
12 | { | |
13 | // // // Low-level I/O handling features: | |
14 | ||
15 | /** | |
16 | * Feature that determines whether parser will automatically | |
17 | * close underlying input source that is NOT owned by the | |
18 | * parser. If disabled, calling application has to separately | |
19 | * close the underlying {@link InputStream} and {@link Reader} | |
20 | * instances used to create the parser. If enabled, parser | |
21 | * will handle closing, as long as parser itself gets closed: | |
22 | * this happens when end-of-input is encountered, or parser | |
23 | * is closed by a call to {@link JsonParser#close}. | |
24 | *<p> | |
25 | * Feature is enabled by default. | |
26 | */ | |
27 | AUTO_CLOSE_SOURCE(JsonParser.Feature.AUTO_CLOSE_SOURCE), | |
28 | ||
29 | // // // Validity checks | |
30 | ||
31 | /** | |
32 | * Feature that determines whether {@link JsonParser} will explicitly | |
33 | * check that no duplicate JSON Object field names are encountered. | |
34 | * If enabled, parser will check all names within context and report | |
35 | * duplicates by throwing a {@link JsonParseException}; if disabled, | |
36 | * parser will not do such checking. Assumption in latter case is | |
37 | * that caller takes care of handling duplicates at a higher level: | |
38 | * data-binding, for example, has features to specify detection to | |
39 | * be done there. | |
40 | *<p> | |
41 | * Note that enabling this feature will incur performance overhead | |
42 | * due to having to store and check additional information: this typically | |
43 | * adds 20-30% to execution time for basic parsing. | |
44 | */ | |
45 | STRICT_DUPLICATE_DETECTION(JsonParser.Feature.STRICT_DUPLICATE_DETECTION), | |
46 | ||
47 | /** | |
48 | * Feature that determines what to do if the underlying data format requires knowledge | |
49 | * of all properties to decode (usually via a Schema), and if no definition is | |
50 | * found for a property that input content contains. | |
51 | * Typically most textual data formats do NOT require schema information (although | |
52 | * some do, such as CSV), whereas many binary data formats do require definitions | |
53 | * (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not). | |
54 | * Further note that some formats that do require schema information will not be able | |
55 | * to ignore undefined properties: for example, Avro is fully positional and there is | |
56 | * no possibility of undefined data. This leaves formats like Protobuf that have identifiers | |
57 | * that may or may not map; and as such Protobuf format does make use of this feature. | |
58 | *<p> | |
59 | * Note that support for this feature is implemented by individual data format | |
60 | * module, if (and only if) it makes sense for the format in question. For JSON, | |
61 | * for example, this feature has no effect as properties need not be pre-defined. | |
62 | *<p> | |
63 | * Feature is disabled by default, meaning that if the underlying data format | |
64 | * requires knowledge of all properties to output, attempts to read an unknown | |
65 | * property will result in a {@link JsonProcessingException} | |
66 | */ | |
67 | IGNORE_UNDEFINED(JsonParser.Feature.IGNORE_UNDEFINED), | |
68 | ||
69 | // // // Other | |
70 | ||
71 | /** | |
72 | * Feature that determines whether {@link JsonLocation} instances should be constructed | |
73 | * with reference to source or not. If source reference is included, its type and contents | |
74 | * are included when `toString()` method is called (most notably when printing out parse | |
75 | * exception with that location information). If feature is disabled, no source reference | |
76 | * is passed and source is only indicated as "UNKNOWN". | |
77 | *<p> | |
78 | * Most common reason for disabling this feature is to avoid leaking information about | |
79 | * internal information; this may be done for security reasons. | |
80 | * Note that even if source reference is included, only parts of contents are usually | |
81 | * printed, and not the whole contents. Further, many source reference types can not | |
82 | * necessarily access contents (like streams), so only type is indicated, not contents. | |
83 | *<p> | |
84 | * Feature is enabled by default, meaning that "source reference" information is passed | |
85 | * and some or all of the source content may be included in {@link JsonLocation} information | |
86 | * constructed either when requested explicitly, or when needed for an exception. | |
87 | */ | |
88 | INCLUDE_SOURCE_IN_LOCATION(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION), | |
89 | ||
90 | ; | |
91 | ||
92 | /** | |
93 | * Whether feature is enabled or disabled by default. | |
94 | */ | |
95 | private final boolean _defaultState; | |
96 | ||
97 | private final int _mask; | |
98 | ||
99 | /** | |
100 | * For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s; | |
101 | * if so, this is the feature to enable/disable. | |
102 | */ | |
103 | final private JsonParser.Feature _mappedFeature; | |
104 | ||
105 | private StreamReadFeature(JsonParser.Feature mapTo) { | |
106 | // only for 2.x, let's map everything to legacy feature: | |
107 | _mappedFeature = mapTo; | |
108 | _mask = mapTo.getMask(); | |
109 | _defaultState = mapTo.enabledByDefault(); | |
110 | } | |
111 | ||
112 | /** | |
113 | * Method that calculates bit set (flags) of all features that | |
114 | * are enabled by default. | |
115 | */ | |
116 | public static int collectDefaults() | |
117 | { | |
118 | int flags = 0; | |
119 | for (StreamReadFeature f : values()) { | |
120 | if (f.enabledByDefault()) { | |
121 | flags |= f.getMask(); | |
122 | } | |
123 | } | |
124 | return flags; | |
125 | } | |
126 | ||
127 | public boolean enabledByDefault() { return _defaultState; } | |
128 | public boolean enabledIn(int flags) { return (flags & _mask) != 0; } | |
129 | public int getMask() { return _mask; } | |
130 | ||
131 | public JsonParser.Feature mappedFeature() { return _mappedFeature; } | |
132 | } |
0 | package com.fasterxml.jackson.core; | |
1 | ||
2 | import java.io.OutputStream; | |
3 | import java.io.Writer; | |
4 | import java.math.BigDecimal; | |
5 | ||
6 | /** | |
7 | * Token writer (generator) features not-specific to any particular format backend. | |
8 | * Eventual replacement for non-JSON-specific {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s. | |
9 | * | |
10 | * @since 2.10 | |
11 | */ | |
12 | public enum StreamWriteFeature | |
13 | { | |
14 | // // Low-level I/O / content features | |
15 | ||
16 | /** | |
17 | * Feature that determines whether generator will automatically | |
18 | * close underlying output target that is NOT owned by the | |
19 | * generator. | |
20 | * If disabled, calling application has to separately | |
21 | * close the underlying {@link OutputStream} and {@link Writer} | |
22 | * instances used to create the generator. If enabled, generator | |
23 | * will handle closing, as long as generator itself gets closed: | |
24 | * this happens when end-of-input is encountered, or generator | |
25 | * is closed by a call to {@link JsonGenerator#close}. | |
26 | *<p> | |
27 | * Feature is enabled by default. | |
28 | */ | |
29 | AUTO_CLOSE_TARGET(JsonGenerator.Feature.AUTO_CLOSE_TARGET), | |
30 | ||
31 | /** | |
32 | * Feature that determines what happens when the generator is | |
33 | * closed while there are still unmatched | |
34 | * {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT} | |
35 | * entries in output content. If enabled, such Array(s) and/or | |
36 | * Object(s) are automatically closed (that is, matching END_ token write | |
37 | * call is made for all open scopes); if disabled, no additional | |
38 | * write calls are made. | |
39 | *<p> | |
40 | * Feature is enabled by default. | |
41 | */ | |
42 | AUTO_CLOSE_CONTENT(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT), | |
43 | ||
44 | /** | |
45 | * Feature that specifies that calls to {@link JsonGenerator#flush} will cause | |
46 | * matching <code>flush()</code> to underlying {@link OutputStream} | |
47 | * or {@link Writer}; if disabled this will not be done. | |
48 | * Main reason to disable this feature is to prevent flushing at | |
49 | * generator level, if it is not possible to prevent method being | |
50 | * called by other code (like <code>ObjectMapper</code> or third | |
51 | * party libraries). | |
52 | *<p> | |
53 | * Feature is enabled by default. | |
54 | */ | |
55 | FLUSH_PASSED_TO_STREAM(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM), | |
56 | ||
57 | // // Datatype coercion features | |
58 | ||
59 | /** | |
60 | * Feature that determines whether {@link java.math.BigDecimal} entries are | |
61 | * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent | |
62 | * values to be written using scientific notation. | |
63 | *<p> | |
64 | * NOTE: only affects generators that serialize {@link java.math.BigDecimal}s | |
65 | * using textual representation (textual formats but potentially some binary | |
66 | * formats). | |
67 | *<p> | |
68 | * Feature is disabled by default, so default output mode is used; this generally | |
69 | * depends on how {@link BigDecimal} has been created. | |
70 | */ | |
71 | WRITE_BIGDECIMAL_AS_PLAIN(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN), | |
72 | ||
73 | // // Schema/Validity support features | |
74 | ||
75 | /** | |
76 | * Feature that determines whether {@link JsonGenerator} will explicitly | |
77 | * check that no duplicate JSON Object field names are written. | |
78 | * If enabled, generator will check all names within context and report | |
79 | * duplicates by throwing a {@link JsonGenerationException}; if disabled, | |
80 | * no such checking will be done. Assumption in latter case is | |
81 | * that caller takes care of not trying to write duplicate names. | |
82 | *<p> | |
83 | * Note that enabling this feature will incur performance overhead | |
84 | * due to having to store and check additional information. | |
85 | *<p> | |
86 | * Feature is disabled by default. | |
87 | */ | |
88 | STRICT_DUPLICATE_DETECTION(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION), | |
89 | ||
90 | /** | |
91 | * Feature that determines what to do if the underlying data format requires knowledge | |
92 | * of all properties to output, and if no definition is found for a property that | |
93 | * caller tries to write. If enabled, such properties will be quietly ignored; | |
94 | * if disabled, a {@link JsonProcessingException} will be thrown to indicate the | |
95 | * problem. | |
96 | * Typically most textual data formats do NOT require schema information (although | |
97 | * some do, such as CSV), whereas many binary data formats do require definitions | |
98 | * (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not). | |
99 | *<p> | |
100 | * Note that support for this feature is implemented by individual data format | |
101 | * module, if (and only if) it makes sense for the format in question. For JSON, | |
102 | * for example, this feature has no effect as properties need not be pre-defined. | |
103 | *<p> | |
104 | * Feature is disabled by default, meaning that if the underlying data format | |
105 | * requires knowledge of all properties to output, attempts to write an unknown | |
106 | * property will result in a {@link JsonProcessingException} | |
107 | */ | |
108 | IGNORE_UNKNOWN(JsonGenerator.Feature.IGNORE_UNKNOWN), | |
109 | ; | |
110 | ||
111 | /** | |
112 | * Whether feature is enabled or disabled by default. | |
113 | */ | |
114 | private final boolean _defaultState; | |
115 | ||
116 | private final int _mask; | |
117 | ||
118 | /** | |
119 | * For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s; | |
120 | * if so, this is the feature to enable/disable. | |
121 | */ | |
122 | final private JsonGenerator.Feature _mappedFeature; | |
123 | ||
124 | private StreamWriteFeature(JsonGenerator.Feature mappedTo) { | |
125 | // only for 2.x, let's map everything to legacy feature: | |
126 | _mappedFeature = mappedTo; | |
127 | _mask = mappedTo.getMask(); | |
128 | _defaultState = mappedTo.enabledByDefault(); | |
129 | } | |
130 | ||
131 | /** | |
132 | * Method that calculates bit set (flags) of all features that | |
133 | * are enabled by default. | |
134 | */ | |
135 | public static int collectDefaults() | |
136 | { | |
137 | int flags = 0; | |
138 | for (StreamWriteFeature f : values()) { | |
139 | if (f.enabledByDefault()) { | |
140 | flags |= f.getMask(); | |
141 | } | |
142 | } | |
143 | return flags; | |
144 | } | |
145 | ||
146 | public boolean enabledByDefault() { return _defaultState; } | |
147 | public boolean enabledIn(int flags) { return (flags & _mask) != 0; } | |
148 | public int getMask() { return _mask; } | |
149 | ||
150 | public JsonGenerator.Feature mappedFeature() { return _mappedFeature; } | |
151 | } |
0 | package com.fasterxml.jackson.core; | |
1 | ||
2 | import com.fasterxml.jackson.core.io.InputDecorator; | |
3 | import com.fasterxml.jackson.core.io.OutputDecorator; | |
4 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
5 | import com.fasterxml.jackson.core.json.JsonWriteFeature; | |
6 | ||
7 | /** | |
8 | * Since 2.10, Builder class is offered for creating token stream factories | |
9 | * with difference configurations: with 3.x they will be fully immutable. | |
10 | * | |
11 | * @since 2.10 | |
12 | */ | |
13 | public abstract class TSFBuilder<F extends JsonFactory, | |
14 | B extends TSFBuilder<F,B>> | |
15 | { | |
16 | /* | |
17 | /********************************************************************** | |
18 | /* Constants | |
19 | /********************************************************************** | |
20 | */ | |
21 | ||
22 | /** | |
23 | * Bitfield (set of flags) of all factory features that are enabled by default. | |
24 | */ | |
25 | protected final static int DEFAULT_FACTORY_FEATURE_FLAGS = JsonFactory.Feature.collectDefaults(); | |
26 | ||
27 | /** | |
28 | * Bitfield (set of flags) of all parser features that are enabled | |
29 | * by default. | |
30 | */ | |
31 | protected final static int DEFAULT_PARSER_FEATURE_FLAGS = JsonParser.Feature.collectDefaults(); | |
32 | ||
33 | /** | |
34 | * Bitfield (set of flags) of all generator features that are enabled | |
35 | * by default. | |
36 | */ | |
37 | protected final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults(); | |
38 | ||
39 | /* | |
40 | /********************************************************************** | |
41 | /* Configured features | |
42 | /********************************************************************** | |
43 | */ | |
44 | ||
45 | /** | |
46 | * Set of {@link TokenStreamFactory.Feature}s enabled, as bitmask. | |
47 | */ | |
48 | protected int _factoryFeatures; | |
49 | ||
50 | /** | |
51 | * Set of {@link JsonParser.Feature}s enabled, as bitmask. | |
52 | */ | |
53 | protected int _streamReadFeatures; | |
54 | ||
55 | /** | |
56 | * Set of {@link JsonGenerator.Feature}s enabled, as bitmask. | |
57 | */ | |
58 | protected int _streamWriteFeatures; | |
59 | ||
60 | /* | |
61 | /********************************************************************** | |
62 | /* Other configuration | |
63 | /********************************************************************** | |
64 | */ | |
65 | ||
66 | /** | |
67 | * Optional helper object that may decorate input sources, to do | |
68 | * additional processing on input during parsing. | |
69 | */ | |
70 | protected InputDecorator _inputDecorator; | |
71 | ||
72 | /** | |
73 | * Optional helper object that may decorate output object, to do | |
74 | * additional processing on output during content generation. | |
75 | */ | |
76 | protected OutputDecorator _outputDecorator; | |
77 | ||
78 | /* | |
79 | /********************************************************************** | |
80 | /* Construction | |
81 | /********************************************************************** | |
82 | */ | |
83 | ||
84 | protected TSFBuilder() { | |
85 | _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS; | |
86 | _streamReadFeatures = DEFAULT_PARSER_FEATURE_FLAGS; | |
87 | _streamWriteFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS; | |
88 | _inputDecorator = null; | |
89 | _outputDecorator = null; | |
90 | } | |
91 | ||
92 | protected TSFBuilder(JsonFactory base) | |
93 | { | |
94 | this(base._factoryFeatures, | |
95 | base._parserFeatures, base._generatorFeatures); | |
96 | } | |
97 | ||
98 | protected TSFBuilder(int factoryFeatures, | |
99 | int parserFeatures, int generatorFeatures) | |
100 | { | |
101 | _factoryFeatures = factoryFeatures; | |
102 | _streamReadFeatures = parserFeatures; | |
103 | _streamWriteFeatures = generatorFeatures; | |
104 | } | |
105 | ||
106 | // // // Accessors | |
107 | ||
108 | public int factoryFeaturesMask() { return _factoryFeatures; } | |
109 | public int streamReadFeatures() { return _streamReadFeatures; } | |
110 | public int streamWriteFeatures() { return _streamWriteFeatures; } | |
111 | ||
112 | public InputDecorator inputDecorator() { return _inputDecorator; } | |
113 | public OutputDecorator outputDecorator() { return _outputDecorator; } | |
114 | ||
115 | // // // Factory features | |
116 | ||
117 | public B enable(JsonFactory.Feature f) { | |
118 | _factoryFeatures |= f.getMask(); | |
119 | return _this(); | |
120 | } | |
121 | ||
122 | public B disable(JsonFactory.Feature f) { | |
123 | _factoryFeatures &= ~f.getMask(); | |
124 | return _this(); | |
125 | } | |
126 | ||
127 | public B configure(JsonFactory.Feature f, boolean state) { | |
128 | return state ? enable(f) : disable(f); | |
129 | } | |
130 | ||
131 | // // // StreamReadFeatures (replacement of non-json-specific parser features) | |
132 | ||
133 | public B enable(StreamReadFeature f) { | |
134 | _streamReadFeatures |= f.mappedFeature().getMask(); | |
135 | return _this(); | |
136 | } | |
137 | ||
138 | public B enable(StreamReadFeature first, StreamReadFeature... other) { | |
139 | _streamReadFeatures |= first.mappedFeature().getMask(); | |
140 | for (StreamReadFeature f : other) { | |
141 | _streamReadFeatures |= f.mappedFeature().getMask(); | |
142 | } | |
143 | return _this(); | |
144 | } | |
145 | ||
146 | public B disable(StreamReadFeature f) { | |
147 | _streamReadFeatures &= ~f.mappedFeature().getMask(); | |
148 | return _this(); | |
149 | } | |
150 | ||
151 | public B disable(StreamReadFeature first, StreamReadFeature... other) { | |
152 | _streamReadFeatures &= ~first.mappedFeature().getMask(); | |
153 | for (StreamReadFeature f : other) { | |
154 | _streamReadFeatures &= ~f.mappedFeature().getMask(); | |
155 | } | |
156 | return _this(); | |
157 | } | |
158 | ||
159 | public B configure(StreamReadFeature f, boolean state) { | |
160 | return state ? enable(f) : disable(f); | |
161 | } | |
162 | ||
163 | // // // StreamWriteFeatures (replacement of non-json-specific generator features) | |
164 | ||
165 | public B enable(StreamWriteFeature f) { | |
166 | _streamWriteFeatures |= f.mappedFeature().getMask(); | |
167 | return _this(); | |
168 | } | |
169 | ||
170 | public B enable(StreamWriteFeature first, StreamWriteFeature... other) { | |
171 | _streamWriteFeatures |= first.mappedFeature().getMask(); | |
172 | for (StreamWriteFeature f : other) { | |
173 | _streamWriteFeatures |= f.mappedFeature().getMask(); | |
174 | } | |
175 | return _this(); | |
176 | } | |
177 | ||
178 | public B disable(StreamWriteFeature f) { | |
179 | _streamWriteFeatures &= ~f.mappedFeature().getMask(); | |
180 | return _this(); | |
181 | } | |
182 | ||
183 | public B disable(StreamWriteFeature first, StreamWriteFeature... other) { | |
184 | _streamWriteFeatures &= ~first.mappedFeature().getMask(); | |
185 | for (StreamWriteFeature f : other) { | |
186 | _streamWriteFeatures &= ~f.mappedFeature().getMask(); | |
187 | } | |
188 | return _this(); | |
189 | } | |
190 | ||
191 | public B configure(StreamWriteFeature f, boolean state) { | |
192 | return state ? enable(f) : disable(f); | |
193 | } | |
194 | ||
195 | /* 26-Jun-2018, tatu: This should not be needed here, but due to 2.x limitations, | |
196 | * we do need to include it or require casting. | |
197 | * Specifically: since `JsonFactory` (and not `TokenStreamFactory`) is base class | |
198 | * for all backends, it can not expose JSON-specific builder, but this. | |
199 | * So let's select lesser evil(s). | |
200 | */ | |
201 | ||
202 | // // // JSON-specific, reads | |
203 | ||
204 | public B enable(JsonReadFeature f) { | |
205 | return _failNonJSON(f); | |
206 | } | |
207 | ||
208 | public B enable(JsonReadFeature first, JsonReadFeature... other) { | |
209 | return _failNonJSON(first); | |
210 | } | |
211 | ||
212 | public B disable(JsonReadFeature f) { | |
213 | return _failNonJSON(f); | |
214 | } | |
215 | ||
216 | public B disable(JsonReadFeature first, JsonReadFeature... other) { | |
217 | return _failNonJSON(first); | |
218 | } | |
219 | ||
220 | public B configure(JsonReadFeature f, boolean state) { | |
221 | return _failNonJSON(f); | |
222 | } | |
223 | ||
224 | private B _failNonJSON(Object feature) { | |
225 | throw new IllegalArgumentException("Feature "+feature.getClass().getName() | |
226 | +"#"+feature.toString()+" not supported for non-JSON backend"); | |
227 | } | |
228 | ||
229 | // // // JSON-specific, writes | |
230 | ||
231 | public B enable(JsonWriteFeature f) { | |
232 | return _failNonJSON(f); | |
233 | } | |
234 | ||
235 | public B enable(JsonWriteFeature first, JsonWriteFeature... other) { | |
236 | return _failNonJSON(first); | |
237 | } | |
238 | ||
239 | public B disable(JsonWriteFeature f) { | |
240 | return _failNonJSON(f); | |
241 | } | |
242 | ||
243 | public B disable(JsonWriteFeature first, JsonWriteFeature... other) { | |
244 | return _failNonJSON(first); | |
245 | } | |
246 | ||
247 | public B configure(JsonWriteFeature f, boolean state) { | |
248 | return _failNonJSON(f); | |
249 | } | |
250 | ||
251 | // // // Other configuration | |
252 | ||
253 | public B inputDecorator(InputDecorator dec) { | |
254 | _inputDecorator = dec; | |
255 | return _this(); | |
256 | } | |
257 | ||
258 | public B outputDecorator(OutputDecorator dec) { | |
259 | _outputDecorator = dec; | |
260 | return _this(); | |
261 | } | |
262 | ||
263 | // // // Other methods | |
264 | ||
265 | /** | |
266 | * Method for constructing actual {@link TokenStreamFactory} instance, given | |
267 | * configuration. | |
268 | */ | |
269 | public abstract F build(); | |
270 | ||
271 | // silly convenience cast method we need | |
272 | @SuppressWarnings("unchecked") | |
273 | protected final B _this() { return (B) this; } | |
274 | ||
275 | // // // Support for subtypes | |
276 | ||
277 | protected void _legacyEnable(JsonParser.Feature f) { | |
278 | _streamReadFeatures |= f.getMask(); | |
279 | } | |
280 | protected void _legacyDisable(JsonParser.Feature f) { | |
281 | _streamReadFeatures &= ~f.getMask(); | |
282 | } | |
283 | ||
284 | protected void _legacyEnable(JsonGenerator.Feature f) { | |
285 | _streamWriteFeatures |= f.getMask(); | |
286 | } | |
287 | protected void _legacyDisable(JsonGenerator.Feature f) { | |
288 | _streamWriteFeatures &= ~f.getMask(); | |
289 | } | |
290 | } |
0 | package com.fasterxml.jackson.core; | |
1 | ||
2 | import java.io.*; | |
3 | import java.net.URL; | |
4 | ||
5 | import com.fasterxml.jackson.core.io.DataOutputAsStream; | |
6 | ||
7 | /** | |
8 | * Intermediate base class for actual format-specific factories for constructing | |
9 | * parsers (reading) and generators (writing). Although full power will only be | |
10 | * available with Jackson 3, skeletal implementation added in 2.10 to help conversion | |
11 | * of code for 2.x to 3.x migration of projects depending on Jackson | |
12 | * | |
13 | * @since 2.10 | |
14 | */ | |
15 | public abstract class TokenStreamFactory | |
16 | implements Versioned, | |
17 | java.io.Serializable | |
18 | { | |
19 | private static final long serialVersionUID = 2; | |
20 | ||
21 | /* | |
22 | /********************************************************** | |
23 | /* Capability introspection | |
24 | /********************************************************** | |
25 | */ | |
26 | ||
27 | /** | |
28 | * Introspection method that higher-level functionality may call | |
29 | * to see whether underlying data format requires a stable ordering | |
30 | * of object properties or not. | |
31 | * This is usually used for determining | |
32 | * whether to force a stable ordering (like alphabetic ordering by name) | |
33 | * if no ordering if explicitly specified. | |
34 | *<p> | |
35 | * Default implementation returns <code>false</code> as JSON does NOT | |
36 | * require stable ordering. Formats that require ordering include positional | |
37 | * textual formats like <code>CSV</code>, and schema-based binary formats | |
38 | * like <code>Avro</code>. | |
39 | */ | |
40 | public abstract boolean requiresPropertyOrdering(); | |
41 | ||
42 | /** | |
43 | * Introspection method that higher-level functionality may call | |
44 | * to see whether underlying data format can read and write binary | |
45 | * data natively; that is, embeded it as-is without using encodings | |
46 | * such as Base64. | |
47 | *<p> | |
48 | * Default implementation returns <code>false</code> as JSON does not | |
49 | * support native access: all binary content must use Base64 encoding. | |
50 | * Most binary formats (like Smile and Avro) support native binary content. | |
51 | */ | |
52 | public abstract boolean canHandleBinaryNatively(); | |
53 | ||
54 | /** | |
55 | * Introspection method that can be used to check whether this | |
56 | * factory can create non-blocking parsers: parsers that do not | |
57 | * use blocking I/O abstractions but instead use a | |
58 | * {@link com.fasterxml.jackson.core.async.NonBlockingInputFeeder}. | |
59 | */ | |
60 | public abstract boolean canParseAsync(); | |
61 | ||
62 | /** | |
63 | * Method for accessing kind of {@link FormatFeature} that a parser | |
64 | * {@link JsonParser} produced by this factory would accept, if any; | |
65 | * <code>null</code> returned if none. | |
66 | * | |
67 | * @since 2.6 | |
68 | */ | |
69 | public abstract Class<? extends FormatFeature> getFormatReadFeatureType(); | |
70 | ||
71 | /** | |
72 | * Method for accessing kind of {@link FormatFeature} that a parser | |
73 | * {@link JsonGenerator} produced by this factory would accept, if any; | |
74 | * <code>null</code> returned if none. | |
75 | * | |
76 | * @since 2.6 | |
77 | */ | |
78 | public abstract Class<? extends FormatFeature> getFormatWriteFeatureType(); | |
79 | ||
80 | /* | |
81 | /********************************************************** | |
82 | /* Format detection functionality | |
83 | /********************************************************** | |
84 | */ | |
85 | ||
86 | /** | |
87 | * Method that can be used to quickly check whether given schema | |
88 | * is something that parsers and/or generators constructed by this | |
89 | * factory could use. Note that this means possible use, at the level | |
90 | * of data format (i.e. schema is for same data format as parsers and | |
91 | * generators this factory constructs); individual schema instances | |
92 | * may have further usage restrictions. | |
93 | * | |
94 | * @since 2.1 | |
95 | */ | |
96 | public abstract boolean canUseSchema(FormatSchema schema); | |
97 | ||
98 | /** | |
99 | * Method that returns short textual id identifying format | |
100 | * this factory supports. | |
101 | */ | |
102 | public abstract String getFormatName(); | |
103 | ||
104 | /* | |
105 | /********************************************************** | |
106 | /* Configuration access | |
107 | /********************************************************** | |
108 | */ | |
109 | ||
110 | public abstract boolean isEnabled(JsonParser.Feature f); | |
111 | public abstract boolean isEnabled(JsonGenerator.Feature f); | |
112 | ||
113 | public abstract int getParserFeatures(); | |
114 | public abstract int getGeneratorFeatures(); | |
115 | ||
116 | public abstract int getFormatParserFeatures(); | |
117 | public abstract int getFormatGeneratorFeatures(); | |
118 | ||
119 | /* | |
120 | /********************************************************** | |
121 | /* Factory methods, parsers | |
122 | /********************************************************** | |
123 | */ | |
124 | ||
125 | public abstract JsonParser createParser(byte[] data) throws IOException; | |
126 | public abstract JsonParser createParser(byte[] data, int offset, int len) throws IOException; | |
127 | public abstract JsonParser createParser(char[] content) throws IOException; | |
128 | public abstract JsonParser createParser(char[] content, int offset, int len) throws IOException; | |
129 | public abstract JsonParser createParser(DataInput in) throws IOException; | |
130 | public abstract JsonParser createParser(File f) throws IOException; | |
131 | public abstract JsonParser createParser(InputStream in) throws IOException; | |
132 | public abstract JsonParser createParser(Reader r) throws IOException; | |
133 | public abstract JsonParser createParser(String content) throws IOException; | |
134 | public abstract JsonParser createParser(URL url) throws IOException; | |
135 | ||
136 | public abstract JsonParser createNonBlockingByteArrayParser() throws IOException; | |
137 | ||
138 | /* | |
139 | /********************************************************** | |
140 | /* Factory methods, generators | |
141 | /********************************************************** | |
142 | */ | |
143 | ||
144 | public abstract JsonGenerator createGenerator(DataOutput out, JsonEncoding enc) throws IOException; | |
145 | public abstract JsonGenerator createGenerator(DataOutput out) throws IOException; | |
146 | public abstract JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOException; | |
147 | public abstract JsonGenerator createGenerator(OutputStream out) throws IOException; | |
148 | public abstract JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException; | |
149 | public abstract JsonGenerator createGenerator(Writer w) throws IOException; | |
150 | ||
151 | /* | |
152 | /********************************************************** | |
153 | /* Internal factory methods, other | |
154 | /********************************************************** | |
155 | */ | |
156 | ||
157 | protected OutputStream _createDataOutputWrapper(DataOutput out) { | |
158 | return new DataOutputAsStream(out); | |
159 | } | |
160 | /** | |
161 | * Helper methods used for constructing an optimal stream for | |
162 | * parsers to use, when input is to be read from an URL. | |
163 | * This helps when reading file content via URL. | |
164 | */ | |
165 | protected InputStream _optimizedStreamFromURL(URL url) throws IOException { | |
166 | if ("file".equals(url.getProtocol())) { | |
167 | /* Can not do this if the path refers | |
168 | * to a network drive on windows. This fixes the problem; | |
169 | * might not be needed on all platforms (NFS?), but should not | |
170 | * matter a lot: performance penalty of extra wrapping is more | |
171 | * relevant when accessing local file system. | |
172 | */ | |
173 | String host = url.getHost(); | |
174 | if (host == null || host.length() == 0) { | |
175 | // [core#48]: Let's try to avoid probs with URL encoded stuff | |
176 | String path = url.getPath(); | |
177 | if (path.indexOf('%') < 0) { | |
178 | return new FileInputStream(url.getPath()); | |
179 | ||
180 | } | |
181 | // otherwise, let's fall through and let URL decoder do its magic | |
182 | } | |
183 | } | |
184 | return url.openStream(); | |
185 | } | |
186 | } |
11 | 11 | { |
12 | 12 | public abstract <T extends TreeNode> T readTree(JsonParser p) throws IOException, JsonProcessingException; |
13 | 13 | public abstract void writeTree(JsonGenerator g, TreeNode tree) throws IOException, JsonProcessingException; |
14 | ||
15 | /** | |
16 | * @since 2.10 | |
17 | */ | |
18 | public TreeNode missingNode() { | |
19 | return null; | |
20 | } | |
21 | ||
22 | /** | |
23 | * @since 2.10 | |
24 | */ | |
25 | public TreeNode nullNode() { | |
26 | return null; | |
27 | } | |
28 | ||
14 | 29 | public abstract TreeNode createArrayNode(); |
15 | 30 | public abstract TreeNode createObjectNode(); |
16 | 31 | public abstract JsonParser treeAsTokens(TreeNode node); |
27 | 27 | * |
28 | 28 | * @since 2.5 |
29 | 29 | */ |
30 | @SuppressWarnings("deprecation") | |
30 | 31 | protected final static int DERIVED_FEATURES_MASK = |
31 | 32 | Feature.WRITE_NUMBERS_AS_STRINGS.getMask() |
32 | 33 | | Feature.ESCAPE_NON_ASCII.getMask() |
99 | 100 | /********************************************************** |
100 | 101 | */ |
101 | 102 | |
103 | @SuppressWarnings("deprecation") | |
102 | 104 | protected GeneratorBase(int features, ObjectCodec codec) { |
103 | 105 | super(); |
104 | 106 | _features = features; |
112 | 114 | /** |
113 | 115 | * @since 2.5 |
114 | 116 | */ |
117 | @SuppressWarnings("deprecation") | |
115 | 118 | protected GeneratorBase(int features, ObjectCodec codec, JsonWriteContext ctxt) { |
116 | 119 | super(); |
117 | 120 | _features = features; |
134 | 137 | |
135 | 138 | @Override |
136 | 139 | public void setCurrentValue(Object v) { |
137 | _writeContext.setCurrentValue(v); | |
140 | if (_writeContext != null) { | |
141 | _writeContext.setCurrentValue(v); | |
142 | } | |
138 | 143 | } |
139 | 144 | |
140 | 145 | /* |
149 | 154 | |
150 | 155 | //public JsonGenerator configure(Feature f, boolean state) { } |
151 | 156 | |
157 | @SuppressWarnings("deprecation") | |
152 | 158 | @Override |
153 | 159 | public JsonGenerator enable(Feature f) { |
154 | 160 | final int mask = f.getMask(); |
168 | 174 | return this; |
169 | 175 | } |
170 | 176 | |
177 | @SuppressWarnings("deprecation") | |
171 | 178 | @Override |
172 | 179 | public JsonGenerator disable(Feature f) { |
173 | 180 | final int mask = f.getMask(); |
216 | 223 | * |
217 | 224 | * @since 2.7 |
218 | 225 | */ |
226 | @SuppressWarnings("deprecation") | |
219 | 227 | protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures) |
220 | 228 | { |
221 | 229 | if ((changedFeatures & DERIVED_FEATURES_MASK) == 0) { |
283 | 291 | public void writeStartObject(Object forValue) throws IOException |
284 | 292 | { |
285 | 293 | writeStartObject(); |
286 | if ((_writeContext != null) && (forValue != null)) { | |
287 | _writeContext.setCurrentValue(forValue); | |
288 | } | |
289 | setCurrentValue(forValue); | |
294 | if (forValue != null) { | |
295 | setCurrentValue(forValue); | |
296 | } | |
290 | 297 | } |
291 | 298 | |
292 | 299 | /* |
5 | 5 | import java.util.Arrays; |
6 | 6 | |
7 | 7 | import com.fasterxml.jackson.core.*; |
8 | import com.fasterxml.jackson.core.JsonParser.Feature; | |
8 | 9 | import com.fasterxml.jackson.core.io.IOContext; |
9 | 10 | import com.fasterxml.jackson.core.io.NumberInput; |
10 | 11 | import com.fasterxml.jackson.core.json.DupDetector; |
844 | 845 | } else { |
845 | 846 | // 16-Oct-2018, tatu: Need to catch "too big" early due to [jackson-core#488] |
846 | 847 | if ((expType == NR_INT) || (expType == NR_LONG)) { |
847 | _reportTooLongInt(expType, numStr); | |
848 | _reportTooLongIntegral(expType, numStr); | |
848 | 849 | } |
849 | 850 | if ((expType == NR_DOUBLE) || (expType == NR_FLOAT)) { |
850 | 851 | _numberDouble = NumberInput.parseDouble(numStr); |
862 | 863 | } |
863 | 864 | |
864 | 865 | // @since 2.9.8 |
865 | protected void _reportTooLongInt(int expType, String rawNum) throws IOException | |
866 | { | |
867 | final String numDesc = _longIntegerDesc(rawNum); | |
868 | _reportError("Numeric value (%s) out of range of %s", numDesc, | |
869 | (expType == NR_LONG) ? "long" : "int"); | |
866 | protected void _reportTooLongIntegral(int expType, String rawNum) throws IOException | |
867 | { | |
868 | if (expType == NR_INT) { | |
869 | reportOverflowInt(rawNum); | |
870 | } else { | |
871 | reportOverflowLong(rawNum); | |
872 | } | |
870 | 873 | } |
871 | 874 | |
872 | 875 | /* |
882 | 885 | // Let's verify it's lossless conversion by simple roundtrip |
883 | 886 | int result = (int) _numberLong; |
884 | 887 | if (((long) result) != _numberLong) { |
885 | _reportError("Numeric value ("+getText()+") out of range of int"); | |
888 | reportOverflowInt(getText(), currentToken()); | |
886 | 889 | } |
887 | 890 | _numberInt = result; |
888 | 891 | } else if ((_numTypesValid & NR_BIGINT) != 0) { |
1014 | 1017 | (char) actCh, expCh, ctxt.typeDesc(), ctxt.getStartLocation(_getSourceReference()))); |
1015 | 1018 | } |
1016 | 1019 | |
1020 | @SuppressWarnings("deprecation") | |
1021 | protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException { | |
1022 | // as per [JACKSON-300] | |
1023 | if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) { | |
1024 | return ch; | |
1025 | } | |
1026 | // and [JACKSON-548] | |
1027 | if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
1028 | return ch; | |
1029 | } | |
1030 | _reportError("Unrecognized character escape "+_getCharDesc(ch)); | |
1031 | return ch; | |
1032 | } | |
1033 | ||
1034 | /** | |
1035 | * Method called to report a problem with unquoted control character. | |
1036 | * Note: it is possible to suppress some instances of | |
1037 | * exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}. | |
1038 | */ | |
1039 | @SuppressWarnings("deprecation") | |
1040 | protected void _throwUnquotedSpace(int i, String ctxtDesc) throws JsonParseException { | |
1041 | // JACKSON-208; possible to allow unquoted control chars: | |
1042 | if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i > INT_SPACE) { | |
1043 | char c = (char) i; | |
1044 | String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc; | |
1045 | _reportError(msg); | |
1046 | } | |
1047 | } | |
1048 | ||
1049 | /** | |
1050 | * @return Description to use as "valid tokens" in an exception message about | |
1051 | * invalid (unrecognized) JSON token: called when parser finds something that | |
1052 | * looks like unquoted textual token | |
1053 | * | |
1054 | * @since 2.10 | |
1055 | */ | |
1056 | protected String _validJsonTokenList() throws IOException { | |
1057 | return _validJsonValueList(); | |
1058 | } | |
1059 | ||
1060 | /** | |
1061 | * @return Description to use as "valid JSON values" in an exception message about | |
1062 | * invalid (unrecognized) JSON value: called when parser finds something that | |
1063 | * does not look like a value or separator. | |
1064 | * | |
1065 | * @since 2.10 | |
1066 | */ | |
1067 | @SuppressWarnings("deprecation") | |
1068 | protected String _validJsonValueList() throws IOException { | |
1069 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
1070 | return "(JSON String, Number (or 'NaN'/'INF'/'+INF'), Array, Object or token 'null', 'true' or 'false')"; | |
1071 | } | |
1072 | return "(JSON String, Number, Array, Object or token 'null', 'true' or 'false')"; | |
1073 | } | |
1074 | ||
1017 | 1075 | /* |
1018 | 1076 | /********************************************************** |
1019 | 1077 | /* Base64 handling support |
4 | 4 | import java.math.BigInteger; |
5 | 5 | |
6 | 6 | import com.fasterxml.jackson.core.*; |
7 | import com.fasterxml.jackson.core.JsonParser.Feature; | |
7 | import com.fasterxml.jackson.core.exc.InputCoercionException; | |
8 | 8 | import com.fasterxml.jackson.core.io.JsonEOFException; |
9 | 9 | import com.fasterxml.jackson.core.io.NumberInput; |
10 | 10 | import com.fasterxml.jackson.core.util.ByteArrayBuilder; |
129 | 129 | * @since 2.9 |
130 | 130 | */ |
131 | 131 | protected final static int MAX_ERROR_TOKEN_LENGTH = 256; |
132 | ||
132 | ||
133 | 133 | /* |
134 | 134 | /********************************************************** |
135 | 135 | /* Minimal generally useful state |
540 | 540 | _reportError(msg); |
541 | 541 | } |
542 | 542 | |
543 | /** | |
544 | * Method called to throw an exception for input token that looks like a number | |
545 | * based on first character(s), but is not valid according to rules of format. | |
546 | * In case of JSON this also includes invalid forms like positive sign and | |
547 | * leading zeroes. | |
548 | */ | |
543 | 549 | protected void reportInvalidNumber(String msg) throws JsonParseException { |
544 | 550 | _reportError("Invalid numeric value: "+msg); |
545 | 551 | } |
546 | 552 | |
553 | /** | |
554 | * Method called to throw an exception for integral (not floating point) input | |
555 | * token with value outside of Java signed 32-bit range when requested as {@link int}. | |
556 | * Result will be {@link InputCoercionException} being thrown. | |
557 | */ | |
547 | 558 | protected void reportOverflowInt() throws IOException { |
548 | _reportError(String.format("Numeric value (%s) out of range of int (%d - %s)", | |
549 | _longIntegerDesc(getText()), Integer.MIN_VALUE, Integer.MAX_VALUE)); | |
550 | } | |
551 | ||
559 | reportOverflowInt(getText()); | |
560 | } | |
561 | ||
562 | // @since 2.10 | |
563 | protected void reportOverflowInt(String numDesc) throws IOException { | |
564 | reportOverflowInt(numDesc, JsonToken.VALUE_NUMBER_INT); | |
565 | } | |
566 | ||
567 | // @since 2.10 | |
568 | protected void reportOverflowInt(String numDesc, JsonToken inputType) throws IOException { | |
569 | _reportInputCoercion(String.format("Numeric value (%s) out of range of int (%d - %s)", | |
570 | _longIntegerDesc(numDesc), Integer.MIN_VALUE, Integer.MAX_VALUE), | |
571 | inputType, Integer.TYPE); | |
572 | } | |
573 | ||
574 | /** | |
575 | * Method called to throw an exception for integral (not floating point) input | |
576 | * token with value outside of Java signed 64-bit range when requested as {@link long}. | |
577 | * Result will be {@link InputCoercionException} being thrown. | |
578 | */ | |
552 | 579 | protected void reportOverflowLong() throws IOException { |
553 | _reportError(String.format("Numeric value (%s) out of range of long (%d - %s)", | |
554 | _longIntegerDesc(getText()), Long.MIN_VALUE, Long.MAX_VALUE)); | |
580 | reportOverflowLong(getText()); | |
581 | } | |
582 | ||
583 | // @since 2.10 | |
584 | protected void reportOverflowLong(String numDesc) throws IOException { | |
585 | reportOverflowLong(numDesc, JsonToken.VALUE_NUMBER_INT); | |
586 | } | |
587 | ||
588 | // @since 2.10 | |
589 | protected void reportOverflowLong(String numDesc, JsonToken inputType) throws IOException { | |
590 | _reportInputCoercion(String.format("Numeric value (%s) out of range of long (%d - %s)", | |
591 | _longIntegerDesc(numDesc), Long.MIN_VALUE, Long.MAX_VALUE), | |
592 | inputType, Long.TYPE); | |
593 | } | |
594 | ||
595 | /** | |
596 | * @since 2.10 | |
597 | */ | |
598 | protected void _reportInputCoercion(String msg, JsonToken inputType, Class<?> targetType) | |
599 | throws InputCoercionException { | |
600 | throw new InputCoercionException(this, msg, inputType, targetType); | |
555 | 601 | } |
556 | 602 | |
557 | 603 | // @since 2.9.8 |
643 | 689 | _reportError(msg); |
644 | 690 | } |
645 | 691 | |
646 | /** | |
647 | * Method called to report a problem with unquoted control character. | |
648 | * Note: starting with version 1.4, it is possible to suppress | |
649 | * exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}. | |
650 | */ | |
651 | protected void _throwUnquotedSpace(int i, String ctxtDesc) throws JsonParseException { | |
652 | // JACKSON-208; possible to allow unquoted control chars: | |
653 | if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i > INT_SPACE) { | |
654 | char c = (char) i; | |
655 | String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc; | |
656 | _reportError(msg); | |
657 | } | |
658 | } | |
659 | ||
660 | protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException { | |
661 | // as per [JACKSON-300] | |
662 | if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) { | |
663 | return ch; | |
664 | } | |
665 | // and [JACKSON-548] | |
666 | if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
667 | return ch; | |
668 | } | |
669 | _reportError("Unrecognized character escape "+_getCharDesc(ch)); | |
670 | return ch; | |
671 | } | |
672 | ||
673 | 692 | /* |
674 | 693 | /********************************************************** |
675 | 694 | /* Error reporting, generic |
0 | package com.fasterxml.jackson.core.exc; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | import com.fasterxml.jackson.core.util.RequestPayload; | |
4 | ||
5 | /** | |
6 | * Exception type for read-side problems that are not direct decoding ("parsing") | |
7 | * problems (those would be reported as {@link com.fasterxml.jackson.core.JsonParseException}s), | |
8 | * but rather result from failed attempts to convert specific Java value out of valid | |
9 | * but incompatible input value. One example is numeric coercions where target number type's | |
10 | * range does not allow mapping of too large/too small input value. | |
11 | * | |
12 | * @since 2.10 | |
13 | */ | |
14 | public class InputCoercionException extends StreamReadException { | |
15 | private static final long serialVersionUID = 1L; | |
16 | ||
17 | /** | |
18 | * Input token that represents input value that failed to coerce. | |
19 | */ | |
20 | protected final JsonToken _inputType; | |
21 | ||
22 | /** | |
23 | * Target type that input value failed to coerce to. | |
24 | */ | |
25 | protected final Class<?> _targetType; | |
26 | ||
27 | /** | |
28 | * Constructor that uses current parsing location as location, and | |
29 | * sets processor (accessible via {@link #getProcessor()}) to | |
30 | * specified parser. | |
31 | */ | |
32 | public InputCoercionException(JsonParser p, String msg, | |
33 | JsonToken inputType, Class<?> targetType) { | |
34 | super(p, msg); | |
35 | _inputType = inputType; | |
36 | _targetType = targetType; | |
37 | } | |
38 | ||
39 | /** | |
40 | * Fluent method that may be used to assign originating {@link JsonParser}, | |
41 | * to be accessed using {@link #getProcessor()}. | |
42 | *<p> | |
43 | * NOTE: `this` instance is modified and no new instance is constructed. | |
44 | */ | |
45 | @Override | |
46 | public InputCoercionException withParser(JsonParser p) { | |
47 | _processor = p; | |
48 | return this; | |
49 | } | |
50 | ||
51 | @Override | |
52 | public InputCoercionException withRequestPayload(RequestPayload p) { | |
53 | _requestPayload = p; | |
54 | return this; | |
55 | } | |
56 | ||
57 | /** | |
58 | * Accessor for getting information about input type (in form of token, giving "shape" | |
59 | * of input) for which coercion failed. | |
60 | */ | |
61 | public JsonToken getInputType() { | |
62 | return _inputType; | |
63 | } | |
64 | ||
65 | /** | |
66 | * Accessor for getting information about target type (in form of Java {@link java.lang.Class}) | |
67 | * for which coercion failed. | |
68 | */ | |
69 | public Class<?> getTargetType() { | |
70 | return _targetType; | |
71 | } | |
72 | } |
0 | package com.fasterxml.jackson.core.exc; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | import com.fasterxml.jackson.core.util.RequestPayload; | |
4 | ||
5 | /** | |
6 | * Intermediate base class for all read-side streaming processing problems, including | |
7 | * parsing and input value coercion problems. | |
8 | * | |
9 | * @since 2.10 | |
10 | */ | |
11 | public abstract class StreamReadException | |
12 | extends JsonProcessingException | |
13 | { | |
14 | final static long serialVersionUID = 1L; | |
15 | ||
16 | protected transient JsonParser _processor; | |
17 | ||
18 | /** | |
19 | * Optional payload that can be assigned to pass along for error reporting | |
20 | * or handling purposes. Core streaming parser implementations DO NOT | |
21 | * initialize this; it is up to using applications and frameworks to | |
22 | * populate it. | |
23 | */ | |
24 | protected RequestPayload _requestPayload; | |
25 | ||
26 | public StreamReadException(JsonParser p, String msg) { | |
27 | super(msg, (p == null) ? null : p.getCurrentLocation()); | |
28 | _processor = p; | |
29 | } | |
30 | ||
31 | public StreamReadException(JsonParser p, String msg, Throwable root) { | |
32 | super(msg, (p == null) ? null : p.getCurrentLocation(), root); | |
33 | _processor = p; | |
34 | } | |
35 | ||
36 | public StreamReadException(JsonParser p, String msg, JsonLocation loc) { | |
37 | super(msg, loc, null); | |
38 | _processor = p; | |
39 | } | |
40 | ||
41 | protected StreamReadException(String msg, JsonLocation loc, Throwable rootCause) { | |
42 | super(msg); | |
43 | if (rootCause != null) { | |
44 | initCause(rootCause); | |
45 | } | |
46 | _location = loc; | |
47 | } | |
48 | ||
49 | /** | |
50 | * Fluent method that may be used to assign originating {@link JsonParser}, | |
51 | * to be accessed using {@link #getProcessor()}. | |
52 | *<p> | |
53 | * NOTE: `this` instance is modified and no new instance is constructed. | |
54 | */ | |
55 | public abstract StreamReadException withParser(JsonParser p); | |
56 | ||
57 | /** | |
58 | * Fluent method that may be used to assign payload to this exception, | |
59 | * to let recipient access it for diagnostics purposes. | |
60 | *<p> | |
61 | * NOTE: `this` instance is modified and no new instance is constructed. | |
62 | */ | |
63 | public abstract StreamReadException withRequestPayload(RequestPayload p); | |
64 | ||
65 | @Override | |
66 | public JsonParser getProcessor() { | |
67 | return _processor; | |
68 | } | |
69 | ||
70 | /** | |
71 | * Method that may be called to find payload that was being parsed, if | |
72 | * one was specified for parser that threw this Exception. | |
73 | * | |
74 | * @return request body, if payload was specified; `null` otherwise | |
75 | */ | |
76 | public RequestPayload getRequestPayload() { | |
77 | return _requestPayload; | |
78 | } | |
79 | ||
80 | /** | |
81 | * The method returns the String representation of the request payload if | |
82 | * one was specified for parser that threw this Exception. | |
83 | * | |
84 | * @return request body as String, if payload was specified; `null` otherwise | |
85 | */ | |
86 | public String getRequestPayloadAsString() { | |
87 | return (_requestPayload != null) ? _requestPayload.toString() : null; | |
88 | } | |
89 | ||
90 | /** | |
91 | * Overriding the getMessage() to include the request body | |
92 | */ | |
93 | @Override | |
94 | public String getMessage() { | |
95 | String msg = super.getMessage(); | |
96 | if (_requestPayload != null) { | |
97 | msg += "\nRequest payload : " + _requestPayload.toString(); | |
98 | } | |
99 | return msg; | |
100 | } | |
101 | } |
0 | /** | |
1 | * Package for some of {@link com.fasterxml.jackson.core.JsonProcessingException} | |
2 | * subtypes contained by streaming API. | |
3 | */ | |
4 | package com.fasterxml.jackson.core.exc; |
160 | 160 | // 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant |
161 | 161 | table[i] = CharacterEscapes.ESCAPE_STANDARD; |
162 | 162 | } |
163 | /* Others (and some within that range too) have explicit shorter | |
164 | * sequences | |
165 | */ | |
163 | // Others (and some within that range too) have explicit shorter sequences | |
166 | 164 | table['"'] = '"'; |
167 | 165 | table['\\'] = '\\'; |
168 | 166 | // Escaping of slash is optional, so let's not add it |
209 | 207 | */ |
210 | 208 | public static int[] get7BitOutputEscapes() { return sOutputEscapes128; } |
211 | 209 | |
210 | /** | |
211 | * Alternative to {@link #get7BitOutputEscapes()} when a non-standard quote character | |
212 | * is used. | |
213 | * | |
214 | * @since 2.10 | |
215 | */ | |
216 | public static int[] get7BitOutputEscapes(int quoteChar) { | |
217 | if (quoteChar == '"') { | |
218 | return sOutputEscapes128; | |
219 | } | |
220 | return AltEscapes.instance.escapesFor(quoteChar); | |
221 | } | |
222 | ||
212 | 223 | public static int charToHex(int ch) |
213 | 224 | { |
214 | 225 | return (ch > 127) ? -1 : sHexValues[ch]; |
254 | 265 | public static byte[] copyHexBytes() { |
255 | 266 | return (byte[]) HB.clone(); |
256 | 267 | } |
268 | ||
269 | // @since 2.10 | |
270 | private static class AltEscapes { | |
271 | public final static AltEscapes instance = new AltEscapes(); | |
272 | ||
273 | private int[][] _altEscapes = new int[128][]; | |
274 | ||
275 | public int[] escapesFor(int quoteChar) { | |
276 | int[] esc = _altEscapes[quoteChar]; | |
277 | if (esc == null) { | |
278 | esc = Arrays.copyOf(sOutputEscapes128, 128); | |
279 | // Only add escape setting if character does not already have it | |
280 | if (esc[quoteChar] == 0) { | |
281 | esc[quoteChar] = CharacterEscapes.ESCAPE_STANDARD; | |
282 | } | |
283 | _altEscapes[quoteChar] = esc; | |
284 | } | |
285 | return esc; | |
286 | } | |
287 | } | |
257 | 288 | } |
258 | 289 |
0 | 0 | package com.fasterxml.jackson.core.io; |
1 | 1 | |
2 | import com.fasterxml.jackson.core.util.BufferRecyclers; | |
2 | import java.util.Arrays; | |
3 | ||
3 | 4 | import com.fasterxml.jackson.core.util.ByteArrayBuilder; |
4 | 5 | import com.fasterxml.jackson.core.util.TextBuffer; |
5 | 6 | |
10 | 11 | * Note that methods in here are somewhat optimized, but not ridiculously so. |
11 | 12 | * Reason is that conversion method results are expected to be cached so that |
12 | 13 | * these methods will not be hot spots during normal operation. |
13 | *<p> | |
14 | * NOTE: starting with 2.9.3, access to most functionality should go through | |
15 | * {@link BufferRecyclers} and NOT directly through this class. | |
16 | 14 | */ |
17 | 15 | public final class JsonStringEncoder |
18 | 16 | { |
17 | /* | |
18 | /********************************************************************** | |
19 | /* Constants | |
20 | /********************************************************************** | |
21 | */ | |
22 | ||
19 | 23 | private final static char[] HC = CharTypes.copyHexChars(); |
20 | 24 | |
21 | 25 | private final static byte[] HB = CharTypes.copyHexBytes(); |
25 | 29 | private final static int SURR2_FIRST = 0xDC00; |
26 | 30 | private final static int SURR2_LAST = 0xDFFF; |
27 | 31 | |
28 | // private final static int INT_BACKSLASH = '\\'; | |
29 | // private final static int INT_U = 'u'; | |
30 | // private final static int INT_0 = '0'; | |
31 | ||
32 | /** | |
33 | * Lazily constructed text buffer used to produce JSON encoded Strings | |
34 | * as characters (without UTF-8 encoding) | |
35 | */ | |
36 | protected TextBuffer _text; | |
37 | ||
38 | /** | |
39 | * Lazily-constructed builder used for UTF-8 encoding of text values | |
40 | * (quoted and unquoted) | |
41 | */ | |
42 | protected ByteArrayBuilder _bytes; | |
32 | private final static int INITIAL_CHAR_BUFFER_SIZE = 120; | |
33 | private final static int INITIAL_BYTE_BUFFER_SIZE = 200; | |
34 | ||
35 | /* | |
36 | /********************************************************************** | |
37 | /* Construction, instance access | |
38 | /********************************************************************** | |
39 | */ | |
40 | ||
41 | // Since 2.10 we have stateless singleton and NO fancy ThreadLocal/SofRef caching!!! | |
42 | private final static JsonStringEncoder instance = new JsonStringEncoder(); | |
43 | 43 | |
44 | /** | |
45 | * Temporary buffer used for composing quote/escape sequences | |
46 | */ | |
47 | protected final char[] _qbuf; | |
48 | ||
49 | /* | |
50 | /********************************************************** | |
51 | /* Construction, instance access | |
52 | /********************************************************** | |
53 | */ | |
54 | ||
55 | public JsonStringEncoder() { | |
56 | _qbuf = new char[6]; | |
57 | _qbuf[0] = '\\'; | |
58 | _qbuf[2] = '0'; | |
59 | _qbuf[3] = '0'; | |
60 | } | |
61 | ||
44 | public JsonStringEncoder() { } | |
45 | ||
62 | 46 | /** |
63 | 47 | * Factory method for getting an instance; this is either recycled per-thread instance, |
64 | 48 | * or a newly constructed one. |
65 | * | |
66 | * @deprecated Since 2.9.2 use {@link BufferRecyclers#getJsonStringEncoder()} instead | |
67 | */ | |
68 | @Deprecated | |
49 | */ | |
69 | 50 | public static JsonStringEncoder getInstance() { |
70 | return BufferRecyclers.getJsonStringEncoder(); | |
51 | return instance; | |
71 | 52 | } |
72 | 53 | |
73 | 54 | /* |
74 | /********************************************************** | |
55 | /********************************************************************** | |
75 | 56 | /* Public API |
76 | /********************************************************** | |
57 | /********************************************************************** | |
77 | 58 | */ |
78 | 59 | |
79 | 60 | /** |
82 | 63 | */ |
83 | 64 | public char[] quoteAsString(String input) |
84 | 65 | { |
85 | TextBuffer textBuffer = _text; | |
66 | char[] outputBuffer = new char[INITIAL_CHAR_BUFFER_SIZE]; | |
67 | final int[] escCodes = CharTypes.get7BitOutputEscapes(); | |
68 | final int escCodeCount = escCodes.length; | |
69 | int inPtr = 0; | |
70 | final int inputLen = input.length(); | |
71 | TextBuffer textBuffer = null; | |
72 | int outPtr = 0; | |
73 | char[] qbuf = null; | |
74 | ||
75 | outer: | |
76 | while (inPtr < inputLen) { | |
77 | tight_loop: | |
78 | while (true) { | |
79 | char c = input.charAt(inPtr); | |
80 | if (c < escCodeCount && escCodes[c] != 0) { | |
81 | break tight_loop; | |
82 | } | |
83 | if (outPtr >= outputBuffer.length) { | |
84 | if (textBuffer == null) { | |
85 | textBuffer = TextBuffer.fromInitial(outputBuffer); | |
86 | } | |
87 | outputBuffer = textBuffer.finishCurrentSegment(); | |
88 | outPtr = 0; | |
89 | } | |
90 | outputBuffer[outPtr++] = c; | |
91 | if (++inPtr >= inputLen) { | |
92 | break outer; | |
93 | } | |
94 | } | |
95 | // something to escape; 2 or 6-char variant? | |
96 | if (qbuf == null) { | |
97 | qbuf = _qbuf(); | |
98 | } | |
99 | char d = input.charAt(inPtr++); | |
100 | int escCode = escCodes[d]; | |
101 | int length = (escCode < 0) | |
102 | ? _appendNumeric(d, qbuf) | |
103 | : _appendNamed(escCode, qbuf); | |
104 | ; | |
105 | if ((outPtr + length) > outputBuffer.length) { | |
106 | int first = outputBuffer.length - outPtr; | |
107 | if (first > 0) { | |
108 | System.arraycopy(qbuf, 0, outputBuffer, outPtr, first); | |
109 | } | |
110 | if (textBuffer == null) { | |
111 | textBuffer = TextBuffer.fromInitial(outputBuffer); | |
112 | } | |
113 | outputBuffer = textBuffer.finishCurrentSegment(); | |
114 | int second = length - first; | |
115 | System.arraycopy(qbuf, first, outputBuffer, 0, second); | |
116 | outPtr = second; | |
117 | } else { | |
118 | System.arraycopy(qbuf, 0, outputBuffer, outPtr, length); | |
119 | outPtr += length; | |
120 | } | |
121 | } | |
122 | ||
86 | 123 | if (textBuffer == null) { |
87 | // no allocator; can add if we must, shouldn't need to | |
88 | _text = textBuffer = new TextBuffer(null); | |
89 | } | |
90 | char[] outputBuffer = textBuffer.emptyAndGetCurrentSegment(); | |
124 | return Arrays.copyOfRange(outputBuffer, 0, outPtr); | |
125 | } | |
126 | textBuffer.setCurrentLength(outPtr); | |
127 | return textBuffer.contentsAsArray(); | |
128 | } | |
129 | ||
130 | /** | |
131 | * Overloaded variant of {@link #quoteAsString(String)}. | |
132 | * | |
133 | * @since 2.10 | |
134 | */ | |
135 | public char[] quoteAsString(CharSequence input) | |
136 | { | |
137 | // 15-Aug-2019, tatu: Optimize common case as JIT can't get rid of overhead otherwise | |
138 | if (input instanceof String) { | |
139 | return quoteAsString((String) input); | |
140 | } | |
141 | ||
142 | TextBuffer textBuffer = null; | |
143 | ||
144 | char[] outputBuffer = new char[INITIAL_CHAR_BUFFER_SIZE]; | |
91 | 145 | final int[] escCodes = CharTypes.get7BitOutputEscapes(); |
92 | 146 | final int escCodeCount = escCodes.length; |
93 | 147 | int inPtr = 0; |
94 | 148 | final int inputLen = input.length(); |
95 | 149 | int outPtr = 0; |
150 | char[] qbuf = null; | |
96 | 151 | |
97 | 152 | outer: |
98 | 153 | while (inPtr < inputLen) { |
103 | 158 | break tight_loop; |
104 | 159 | } |
105 | 160 | if (outPtr >= outputBuffer.length) { |
161 | if (textBuffer == null) { | |
162 | textBuffer = TextBuffer.fromInitial(outputBuffer); | |
163 | } | |
106 | 164 | outputBuffer = textBuffer.finishCurrentSegment(); |
107 | 165 | outPtr = 0; |
108 | 166 | } |
112 | 170 | } |
113 | 171 | } |
114 | 172 | // something to escape; 2 or 6-char variant? |
173 | if (qbuf == null) { | |
174 | qbuf = _qbuf(); | |
175 | } | |
115 | 176 | char d = input.charAt(inPtr++); |
116 | 177 | int escCode = escCodes[d]; |
117 | 178 | int length = (escCode < 0) |
118 | ? _appendNumeric(d, _qbuf) | |
119 | : _appendNamed(escCode, _qbuf); | |
179 | ? _appendNumeric(d, qbuf) | |
180 | : _appendNamed(escCode, qbuf); | |
120 | 181 | ; |
121 | 182 | if ((outPtr + length) > outputBuffer.length) { |
122 | 183 | int first = outputBuffer.length - outPtr; |
123 | 184 | if (first > 0) { |
124 | System.arraycopy(_qbuf, 0, outputBuffer, outPtr, first); | |
185 | System.arraycopy(qbuf, 0, outputBuffer, outPtr, first); | |
186 | } | |
187 | if (textBuffer == null) { | |
188 | textBuffer = TextBuffer.fromInitial(outputBuffer); | |
125 | 189 | } |
126 | 190 | outputBuffer = textBuffer.finishCurrentSegment(); |
127 | 191 | int second = length - first; |
128 | System.arraycopy(_qbuf, first, outputBuffer, 0, second); | |
192 | System.arraycopy(qbuf, first, outputBuffer, 0, second); | |
129 | 193 | outPtr = second; |
130 | 194 | } else { |
131 | System.arraycopy(_qbuf, 0, outputBuffer, outPtr, length); | |
195 | System.arraycopy(qbuf, 0, outputBuffer, outPtr, length); | |
132 | 196 | outPtr += length; |
133 | 197 | } |
198 | } | |
199 | ||
200 | if (textBuffer == null) { | |
201 | return Arrays.copyOfRange(outputBuffer, 0, outPtr); | |
134 | 202 | } |
135 | 203 | textBuffer.setCurrentLength(outPtr); |
136 | 204 | return textBuffer.contentsAsArray(); |
149 | 217 | final int escCodeCount = escCodes.length; |
150 | 218 | int inPtr = 0; |
151 | 219 | final int inputLen = input.length(); |
220 | char[] qbuf = null; | |
152 | 221 | |
153 | 222 | outer: |
154 | 223 | while (inPtr < inputLen) { |
164 | 233 | } |
165 | 234 | } |
166 | 235 | // something to escape; 2 or 6-char variant? |
236 | if (qbuf == null) { | |
237 | qbuf = _qbuf(); | |
238 | } | |
167 | 239 | char d = input.charAt(inPtr++); |
168 | 240 | int escCode = escCodes[d]; |
169 | 241 | int length = (escCode < 0) |
170 | ? _appendNumeric(d, _qbuf) | |
171 | : _appendNamed(escCode, _qbuf); | |
172 | ; | |
173 | output.append(_qbuf, 0, length); | |
242 | ? _appendNumeric(d, qbuf) | |
243 | : _appendNamed(escCode, qbuf); | |
244 | output.append(qbuf, 0, length); | |
174 | 245 | } |
175 | 246 | } |
176 | 247 | |
181 | 252 | @SuppressWarnings("resource") |
182 | 253 | public byte[] quoteAsUTF8(String text) |
183 | 254 | { |
184 | ByteArrayBuilder bb = _bytes; | |
185 | if (bb == null) { | |
186 | // no allocator; can add if we must, shouldn't need to | |
187 | _bytes = bb = new ByteArrayBuilder(null); | |
188 | } | |
189 | 255 | int inputPtr = 0; |
190 | 256 | int inputEnd = text.length(); |
191 | 257 | int outputPtr = 0; |
192 | byte[] outputBuffer = bb.resetAndGetFirstSegment(); | |
258 | byte[] outputBuffer = new byte[INITIAL_BYTE_BUFFER_SIZE]; | |
259 | ByteArrayBuilder bb = null; | |
193 | 260 | |
194 | 261 | main: |
195 | 262 | while (inputPtr < inputEnd) { |
202 | 269 | break inner_loop; |
203 | 270 | } |
204 | 271 | if (outputPtr >= outputBuffer.length) { |
272 | if (bb == null) { | |
273 | bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); | |
274 | } | |
205 | 275 | outputBuffer = bb.finishCurrentSegment(); |
206 | 276 | outputPtr = 0; |
207 | 277 | } |
209 | 279 | if (++inputPtr >= inputEnd) { |
210 | 280 | break main; |
211 | 281 | } |
212 | } | |
282 | } | |
283 | if (bb == null) { | |
284 | bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); | |
285 | } | |
213 | 286 | if (outputPtr >= outputBuffer.length) { |
214 | 287 | outputBuffer = bb.finishCurrentSegment(); |
215 | 288 | outputPtr = 0; |
268 | 341 | } |
269 | 342 | outputBuffer[outputPtr++] = (byte) ch; |
270 | 343 | } |
271 | return _bytes.completeAndCoalesce(outputPtr); | |
272 | } | |
273 | ||
344 | if (bb == null) { | |
345 | return Arrays.copyOfRange(outputBuffer, 0, outputPtr); | |
346 | } | |
347 | return bb.completeAndCoalesce(outputPtr); | |
348 | } | |
349 | ||
274 | 350 | /** |
275 | 351 | * Will encode given String as UTF-8 (without any quoting), return |
276 | 352 | * resulting byte array. |
278 | 354 | @SuppressWarnings("resource") |
279 | 355 | public byte[] encodeAsUTF8(String text) |
280 | 356 | { |
281 | ByteArrayBuilder byteBuilder = _bytes; | |
282 | if (byteBuilder == null) { | |
283 | // no allocator; can add if we must, shouldn't need to | |
284 | _bytes = byteBuilder = new ByteArrayBuilder(null); | |
285 | } | |
286 | 357 | int inputPtr = 0; |
287 | 358 | int inputEnd = text.length(); |
288 | 359 | int outputPtr = 0; |
289 | byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment(); | |
360 | byte[] outputBuffer = new byte[INITIAL_BYTE_BUFFER_SIZE]; | |
290 | 361 | int outputEnd = outputBuffer.length; |
291 | ||
362 | ByteArrayBuilder bb = null; | |
363 | ||
292 | 364 | main_loop: |
293 | 365 | while (inputPtr < inputEnd) { |
294 | 366 | int c = text.charAt(inputPtr++); |
296 | 368 | // first tight loop for ascii |
297 | 369 | while (c <= 0x7F) { |
298 | 370 | if (outputPtr >= outputEnd) { |
299 | outputBuffer = byteBuilder.finishCurrentSegment(); | |
371 | if (bb == null) { | |
372 | bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); | |
373 | } | |
374 | outputBuffer = bb.finishCurrentSegment(); | |
300 | 375 | outputEnd = outputBuffer.length; |
301 | 376 | outputPtr = 0; |
302 | 377 | } |
308 | 383 | } |
309 | 384 | |
310 | 385 | // then multi-byte... |
386 | if (bb == null) { | |
387 | bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); | |
388 | } | |
311 | 389 | if (outputPtr >= outputEnd) { |
312 | outputBuffer = byteBuilder.finishCurrentSegment(); | |
390 | outputBuffer = bb.finishCurrentSegment(); | |
313 | 391 | outputEnd = outputBuffer.length; |
314 | 392 | outputPtr = 0; |
315 | 393 | } |
320 | 398 | if (c < SURR1_FIRST || c > SURR2_LAST) { // nope |
321 | 399 | outputBuffer[outputPtr++] = (byte) (0xe0 | (c >> 12)); |
322 | 400 | if (outputPtr >= outputEnd) { |
323 | outputBuffer = byteBuilder.finishCurrentSegment(); | |
401 | outputBuffer = bb.finishCurrentSegment(); | |
324 | 402 | outputEnd = outputBuffer.length; |
325 | 403 | outputPtr = 0; |
326 | 404 | } |
339 | 417 | } |
340 | 418 | outputBuffer[outputPtr++] = (byte) (0xf0 | (c >> 18)); |
341 | 419 | if (outputPtr >= outputEnd) { |
342 | outputBuffer = byteBuilder.finishCurrentSegment(); | |
420 | outputBuffer = bb.finishCurrentSegment(); | |
343 | 421 | outputEnd = outputBuffer.length; |
344 | 422 | outputPtr = 0; |
345 | 423 | } |
346 | 424 | outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); |
347 | 425 | if (outputPtr >= outputEnd) { |
348 | outputBuffer = byteBuilder.finishCurrentSegment(); | |
426 | outputBuffer = bb.finishCurrentSegment(); | |
349 | 427 | outputEnd = outputBuffer.length; |
350 | 428 | outputPtr = 0; |
351 | 429 | } |
353 | 431 | } |
354 | 432 | } |
355 | 433 | if (outputPtr >= outputEnd) { |
356 | outputBuffer = byteBuilder.finishCurrentSegment(); | |
434 | outputBuffer = bb.finishCurrentSegment(); | |
357 | 435 | outputEnd = outputBuffer.length; |
358 | 436 | outputPtr = 0; |
359 | 437 | } |
360 | 438 | outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f)); |
361 | 439 | } |
362 | return _bytes.completeAndCoalesce(outputPtr); | |
363 | } | |
364 | ||
440 | if (bb == null) { | |
441 | return Arrays.copyOfRange(outputBuffer, 0, outputPtr); | |
442 | } | |
443 | return bb.completeAndCoalesce(outputPtr); | |
444 | } | |
445 | ||
365 | 446 | /* |
366 | /********************************************************** | |
447 | /********************************************************************** | |
367 | 448 | /* Internal methods |
368 | /********************************************************** | |
369 | */ | |
449 | /********************************************************************** | |
450 | */ | |
451 | ||
452 | private char[] _qbuf() { | |
453 | char[] qbuf = new char[6]; | |
454 | qbuf[0] = '\\'; | |
455 | qbuf[2] = '0'; | |
456 | qbuf[3] = '0'; | |
457 | return qbuf; | |
458 | } | |
370 | 459 | |
371 | 460 | private int _appendNumeric(int value, char[] qbuf) { |
372 | 461 | qbuf[1] = 'u'; |
26 | 26 | */ |
27 | 27 | public static int parseInt(char[] ch, int off, int len) |
28 | 28 | { |
29 | int num = ch[off] - '0'; | |
30 | ||
31 | if (len > 4) { | |
32 | num = (num * 10) + (ch[++off] - '0'); | |
33 | num = (num * 10) + (ch[++off] - '0'); | |
34 | num = (num * 10) + (ch[++off] - '0'); | |
35 | num = (num * 10) + (ch[++off] - '0'); | |
36 | len -= 4; | |
37 | if (len > 4) { | |
38 | num = (num * 10) + (ch[++off] - '0'); | |
39 | num = (num * 10) + (ch[++off] - '0'); | |
40 | num = (num * 10) + (ch[++off] - '0'); | |
41 | num = (num * 10) + (ch[++off] - '0'); | |
42 | return num; | |
43 | } | |
44 | } | |
45 | if (len > 1) { | |
46 | num = (num * 10) + (ch[++off] - '0'); | |
47 | if (len > 2) { | |
48 | num = (num * 10) + (ch[++off] - '0'); | |
49 | if (len > 3) { | |
50 | num = (num * 10) + (ch[++off] - '0'); | |
51 | } | |
52 | } | |
29 | int num = ch[off + len - 1] - '0'; | |
30 | ||
31 | switch(len) { | |
32 | case 9: | |
33 | num += (ch[off++] - '0') * 100000000; | |
34 | case 8: | |
35 | num += (ch[off++] - '0') * 10000000; | |
36 | case 7: | |
37 | num += (ch[off++] - '0') * 1000000; | |
38 | case 6: | |
39 | num += (ch[off++] - '0') * 100000; | |
40 | case 5: | |
41 | num += (ch[off++] - '0') * 10000; | |
42 | case 4: | |
43 | num += (ch[off++] - '0') * 1000; | |
44 | case 3: | |
45 | num += (ch[off++] - '0') * 100; | |
46 | case 2: | |
47 | num += (ch[off] - '0') * 10; | |
53 | 48 | } |
54 | 49 | return num; |
55 | 50 | } |
266 | 266 | |
267 | 267 | /* |
268 | 268 | /********************************************************** |
269 | /* Other convenience methods | |
270 | /********************************************************** | |
271 | */ | |
272 | ||
273 | /** | |
274 | * Helper method to verify whether given {@code double} value is finite | |
275 | * (regular rational number} or not (NaN or Infinity). | |
276 | * | |
277 | * @return True if number is NOT finite (is Infinity or NaN); false otherwise | |
278 | * | |
279 | * Since 2.10 | |
280 | */ | |
281 | public static boolean notFinite(double value) { | |
282 | // before Java 8 need separate checks | |
283 | return Double.isNaN(value) || Double.isInfinite(value); | |
284 | } | |
285 | ||
286 | /** | |
287 | * Helper method to verify whether given {@code float} value is finite | |
288 | * (regular rational number} or not (NaN or Infinity). | |
289 | * | |
290 | * @return True if number is NOT finite (is Infinity or NaN); false otherwise | |
291 | * | |
292 | * Since 2.10 | |
293 | */ | |
294 | public static boolean notFinite(float value) { | |
295 | // before Java 8 need separate checks | |
296 | return Float.isNaN(value) || Float.isInfinite(value); | |
297 | } | |
298 | ||
299 | /* | |
300 | /********************************************************** | |
269 | 301 | /* Internal helper methods |
270 | 302 | /********************************************************** |
271 | 303 | */ |
3 | 3 | import java.nio.ByteBuffer; |
4 | 4 | |
5 | 5 | import com.fasterxml.jackson.core.SerializableString; |
6 | import com.fasterxml.jackson.core.util.BufferRecyclers; | |
7 | 6 | |
8 | 7 | /** |
9 | 8 | * String token that can lazily serialize String contained and then reuse that |
19 | 18 | { |
20 | 19 | private static final long serialVersionUID = 1L; |
21 | 20 | |
21 | private static final JsonStringEncoder JSON_ENCODER = JsonStringEncoder.getInstance(); | |
22 | ||
22 | 23 | protected final String _value; |
23 | 24 | |
24 | 25 | /* 13-Dec-2010, tatu: Whether use volatile or not is actually an important |
93 | 94 | */ |
94 | 95 | @Override |
95 | 96 | public final int charLength() { return _value.length(); } |
96 | ||
97 | ||
98 | /** | |
99 | * Accessor for accessing value that has been quoted (escaped) using JSON | |
100 | * quoting rules (using backslash-prefixed codes) into a char array. | |
101 | */ | |
97 | 102 | @Override |
98 | 103 | public final char[] asQuotedChars() { |
99 | 104 | char[] result = _quotedChars; |
100 | 105 | if (result == null) { |
101 | result = BufferRecyclers.quoteAsJsonText(_value); | |
102 | _quotedChars = result; | |
106 | _quotedChars = result = JSON_ENCODER.quoteAsString(_value); | |
103 | 107 | } |
104 | 108 | return result; |
105 | 109 | } |
106 | 110 | |
107 | 111 | /** |
108 | * Accessor for accessing value that has been quoted using JSON | |
109 | * quoting rules, and encoded using UTF-8 encoding. | |
112 | * Accessor for accessing value that has been quoted (escaped) using JSON | |
113 | * quoting rules (using backslash-prefixed codes), and encoded using | |
114 | * UTF-8 encoding into a byte array. | |
115 | */ | |
116 | @Override | |
117 | public final byte[] asQuotedUTF8() { | |
118 | byte[] result = _quotedUTF8Ref; | |
119 | if (result == null) { | |
120 | _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); | |
121 | } | |
122 | return result; | |
123 | } | |
124 | ||
125 | /** | |
126 | * Accessor for accessing value as is (without JSON quoting (ecaping)) | |
127 | * encoded as UTF-8 byte array. | |
110 | 128 | */ |
111 | 129 | @Override |
112 | 130 | public final byte[] asUnquotedUTF8() { |
113 | 131 | byte[] result = _unquotedUTF8Ref; |
114 | 132 | if (result == null) { |
115 | result = BufferRecyclers.encodeAsUTF8(_value); | |
116 | _unquotedUTF8Ref = result; | |
133 | _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); | |
117 | 134 | } |
118 | 135 | return result; |
119 | 136 | } |
120 | 137 | |
121 | /** | |
122 | * Accessor for accessing value as is (without JSON quoting) | |
123 | * encoded using UTF-8 encoding. | |
124 | */ | |
125 | @Override | |
126 | public final byte[] asQuotedUTF8() { | |
127 | byte[] result = _quotedUTF8Ref; | |
128 | if (result == null) { | |
129 | result = BufferRecyclers.quoteAsJsonUTF8(_value); | |
130 | _quotedUTF8Ref = result; | |
131 | } | |
132 | return result; | |
133 | } | |
134 | ||
135 | 138 | /* |
136 | 139 | /********************************************************** |
137 | 140 | /* Additional 2.0 methods for appending/writing contents |
138 | 141 | /********************************************************** |
139 | 142 | */ |
140 | ||
141 | @Override | |
142 | public int appendQuotedUTF8(byte[] buffer, int offset) { | |
143 | byte[] result = _quotedUTF8Ref; | |
144 | if (result == null) { | |
145 | result = BufferRecyclers.quoteAsJsonUTF8(_value); | |
146 | _quotedUTF8Ref = result; | |
147 | } | |
148 | final int length = result.length; | |
149 | if ((offset + length) > buffer.length) { | |
150 | return -1; | |
151 | } | |
152 | System.arraycopy(result, 0, buffer, offset, length); | |
153 | return length; | |
154 | } | |
155 | 143 | |
156 | 144 | @Override |
157 | 145 | public int appendQuoted(char[] buffer, int offset) { |
158 | 146 | char[] result = _quotedChars; |
159 | 147 | if (result == null) { |
160 | result = BufferRecyclers.quoteAsJsonText(_value); | |
161 | _quotedChars = result; | |
148 | _quotedChars = result = JSON_ENCODER.quoteAsString(_value); | |
162 | 149 | } |
163 | 150 | final int length = result.length; |
164 | 151 | if ((offset + length) > buffer.length) { |
169 | 156 | } |
170 | 157 | |
171 | 158 | @Override |
172 | public int appendUnquotedUTF8(byte[] buffer, int offset) { | |
173 | byte[] result = _unquotedUTF8Ref; | |
174 | if (result == null) { | |
175 | result = BufferRecyclers.encodeAsUTF8(_value); | |
176 | _unquotedUTF8Ref = result; | |
159 | public int appendQuotedUTF8(byte[] buffer, int offset) { | |
160 | byte[] result = _quotedUTF8Ref; | |
161 | if (result == null) { | |
162 | _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); | |
177 | 163 | } |
178 | 164 | final int length = result.length; |
179 | 165 | if ((offset + length) > buffer.length) { |
195 | 181 | } |
196 | 182 | |
197 | 183 | @Override |
184 | public int appendUnquotedUTF8(byte[] buffer, int offset) { | |
185 | byte[] result = _unquotedUTF8Ref; | |
186 | if (result == null) { | |
187 | _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); | |
188 | } | |
189 | final int length = result.length; | |
190 | if ((offset + length) > buffer.length) { | |
191 | return -1; | |
192 | } | |
193 | System.arraycopy(result, 0, buffer, offset, length); | |
194 | return length; | |
195 | } | |
196 | ||
197 | @Override | |
198 | 198 | public int writeQuotedUTF8(OutputStream out) throws IOException { |
199 | 199 | byte[] result = _quotedUTF8Ref; |
200 | 200 | if (result == null) { |
201 | result = BufferRecyclers.quoteAsJsonUTF8(_value); | |
202 | _quotedUTF8Ref = result; | |
201 | _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); | |
203 | 202 | } |
204 | 203 | final int length = result.length; |
205 | 204 | out.write(result, 0, length); |
210 | 209 | public int writeUnquotedUTF8(OutputStream out) throws IOException { |
211 | 210 | byte[] result = _unquotedUTF8Ref; |
212 | 211 | if (result == null) { |
213 | result = BufferRecyclers.encodeAsUTF8(_value); | |
214 | _unquotedUTF8Ref = result; | |
212 | _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); | |
215 | 213 | } |
216 | 214 | final int length = result.length; |
217 | 215 | out.write(result, 0, length); |
222 | 220 | public int putQuotedUTF8(ByteBuffer buffer) { |
223 | 221 | byte[] result = _quotedUTF8Ref; |
224 | 222 | if (result == null) { |
225 | result = BufferRecyclers.quoteAsJsonUTF8(_value); | |
226 | _quotedUTF8Ref = result; | |
223 | _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); | |
227 | 224 | } |
228 | 225 | final int length = result.length; |
229 | 226 | if (length > buffer.remaining()) { |
237 | 234 | public int putUnquotedUTF8(ByteBuffer buffer) { |
238 | 235 | byte[] result = _unquotedUTF8Ref; |
239 | 236 | if (result == null) { |
240 | result = BufferRecyclers.encodeAsUTF8(_value); | |
241 | _unquotedUTF8Ref = result; | |
237 | _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); | |
242 | 238 | } |
243 | 239 | final int length = result.length; |
244 | 240 | if (length > buffer.remaining()) { |
248 | 244 | return length; |
249 | 245 | } |
250 | 246 | |
251 | ||
252 | 247 | /* |
253 | 248 | /********************************************************** |
254 | 249 | /* Standard method overrides |
242 | 242 | ByteQuadsCanonicalizer rootByteSymbols, CharsToNameCanonicalizer rootCharSymbols, |
243 | 243 | int factoryFeatures) throws IOException |
244 | 244 | { |
245 | int prevInputPtr = _inputPtr; | |
245 | 246 | JsonEncoding enc = detectEncoding(); |
247 | int bytesProcessed = _inputPtr - prevInputPtr; | |
246 | 248 | |
247 | 249 | if (enc == JsonEncoding.UTF8) { |
248 | 250 | /* and without canonicalization, byte-based approach is not performant; just use std UTF-8 reader |
251 | 253 | if (JsonFactory.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(factoryFeatures)) { |
252 | 254 | ByteQuadsCanonicalizer can = rootByteSymbols.makeChild(factoryFeatures); |
253 | 255 | return new UTF8StreamJsonParser(_context, parserFeatures, _in, codec, can, |
254 | _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable); | |
256 | _inputBuffer, _inputPtr, _inputEnd, bytesProcessed, _bufferRecyclable); | |
255 | 257 | } |
256 | 258 | } |
257 | 259 | return new ReaderBasedJsonParser(_context, parserFeatures, constructReader(), codec, |
97 | 97 | /********************************************************** |
98 | 98 | */ |
99 | 99 | |
100 | @SuppressWarnings("deprecation") | |
100 | 101 | public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec) |
101 | 102 | { |
102 | 103 | super(features, codec); |
125 | 126 | /********************************************************** |
126 | 127 | */ |
127 | 128 | |
129 | @SuppressWarnings("deprecation") | |
128 | 130 | @Override |
129 | 131 | public JsonGenerator enable(Feature f) { |
130 | 132 | super.enable(f); |
134 | 136 | return this; |
135 | 137 | } |
136 | 138 | |
139 | @SuppressWarnings("deprecation") | |
137 | 140 | @Override |
138 | 141 | public JsonGenerator disable(Feature f) { |
139 | 142 | super.disable(f); |
143 | 146 | return this; |
144 | 147 | } |
145 | 148 | |
149 | @SuppressWarnings("deprecation") | |
146 | 150 | @Override |
147 | 151 | protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures) { |
148 | 152 | super._checkStdFeatureChanges(newFeatureFlags, changedFeatures); |
0 | package com.fasterxml.jackson.core.json; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | /** | |
5 | * Token reader (parser) features specific to JSON backend. | |
6 | * Eventual replacement for JSON-specific {@link com.fasterxml.jackson.core.JsonParser.Feature}s. | |
7 | * | |
8 | * @since 2.10 | |
9 | */ | |
10 | public enum JsonReadFeature | |
11 | implements FormatFeature | |
12 | { | |
13 | // // // Support for non-standard data format constructs: comments | |
14 | ||
15 | /** | |
16 | * Feature that determines whether parser will allow use | |
17 | * of Java/C/C++ style comments (both '/'+'*' and | |
18 | * '//' varieties) within parsed content or not. | |
19 | *<p> | |
20 | * Since JSON specification does not mention comments as legal | |
21 | * construct, | |
22 | * this is a non-standard feature; however, in the wild | |
23 | * this is extensively used. As such, feature is | |
24 | * <b>disabled by default</b> for parsers and must be | |
25 | * explicitly enabled. | |
26 | */ | |
27 | ALLOW_JAVA_COMMENTS(false, JsonParser.Feature.ALLOW_COMMENTS), | |
28 | ||
29 | /** | |
30 | * Feature that determines whether parser will allow use | |
31 | * of YAML comments, ones starting with '#' and continuing | |
32 | * until the end of the line. This commenting style is common | |
33 | * with scripting languages as well. | |
34 | *<p> | |
35 | * Since JSON specification does not mention comments as legal | |
36 | * construct, | |
37 | * this is a non-standard feature. As such, feature is | |
38 | * <b>disabled by default</b> for parsers and must be | |
39 | * explicitly enabled. | |
40 | */ | |
41 | ALLOW_YAML_COMMENTS(false, JsonParser.Feature.ALLOW_YAML_COMMENTS), | |
42 | ||
43 | // // // Support for non-standard data format constructs: quoting/escaping | |
44 | ||
45 | /** | |
46 | * Feature that determines whether parser will allow use | |
47 | * of single quotes (apostrophe, character '\'') for | |
48 | * quoting Strings (names and String values). If so, | |
49 | * this is in addition to other acceptable markers. | |
50 | * but not by JSON specification). | |
51 | *<p> | |
52 | * Since JSON specification requires use of double quotes for | |
53 | * field names, | |
54 | * this is a non-standard feature, and as such disabled by default. | |
55 | */ | |
56 | ALLOW_SINGLE_QUOTES(false, JsonParser.Feature.ALLOW_SINGLE_QUOTES), | |
57 | ||
58 | /** | |
59 | * Feature that determines whether parser will allow use | |
60 | * of unquoted field names (which is allowed by Javascript, | |
61 | * but not by JSON specification). | |
62 | *<p> | |
63 | * Since JSON specification requires use of double quotes for | |
64 | * field names, | |
65 | * this is a non-standard feature, and as such disabled by default. | |
66 | */ | |
67 | ALLOW_UNQUOTED_FIELD_NAMES(false, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES), | |
68 | ||
69 | /** | |
70 | * Feature that determines whether parser will allow | |
71 | * JSON Strings to contain unescaped control characters | |
72 | * (ASCII characters with value less than 32, including | |
73 | * tab and line feed characters) or not. | |
74 | * If feature is set false, an exception is thrown if such a | |
75 | * character is encountered. | |
76 | *<p> | |
77 | * Since JSON specification requires quoting for all control characters, | |
78 | * this is a non-standard feature, and as such disabled by default. | |
79 | */ | |
80 | @SuppressWarnings("deprecation") | |
81 | ALLOW_UNESCAPED_CONTROL_CHARS(false, JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS), | |
82 | ||
83 | /** | |
84 | * Feature that can be enabled to accept quoting of all character | |
85 | * using backslash quoting mechanism: if not enabled, only characters | |
86 | * that are explicitly listed by JSON specification can be thus | |
87 | * escaped (see JSON spec for small list of these characters) | |
88 | *<p> | |
89 | * Since JSON specification requires quoting for all control characters, | |
90 | * this is a non-standard feature, and as such disabled by default. | |
91 | */ | |
92 | @SuppressWarnings("deprecation") | |
93 | ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false, JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER), | |
94 | ||
95 | // // // Support for non-standard data format constructs: number representations | |
96 | ||
97 | /** | |
98 | * Feature that determines whether parser will allow | |
99 | * JSON integral numbers to start with additional (ignorable) | |
100 | * zeroes (like: 000001). If enabled, no exception is thrown, and extra | |
101 | * nulls are silently ignored (and not included in textual representation | |
102 | * exposed via {@link JsonParser#getText}). | |
103 | *<p> | |
104 | * Since JSON specification does not allow leading zeroes, | |
105 | * this is a non-standard feature, and as such disabled by default. | |
106 | */ | |
107 | @SuppressWarnings("deprecation") | |
108 | ALLOW_LEADING_ZEROS_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS), | |
109 | ||
110 | /** | |
111 | * Feature that allows parser to recognize set of | |
112 | * "Not-a-Number" (NaN) tokens as legal floating number | |
113 | * values (similar to how many other data formats and | |
114 | * programming language source code allows it). | |
115 | * Specific subset contains values that | |
116 | * <a href="http://www.w3.org/TR/xmlschema-2/">XML Schema</a> | |
117 | * (see section 3.2.4.1, Lexical Representation) | |
118 | * allows (tokens are quoted contents, not including quotes): | |
119 | *<ul> | |
120 | * <li>"INF" (for positive infinity), as well as alias of "Infinity" | |
121 | * <li>"-INF" (for negative infinity), alias "-Infinity" | |
122 | * <li>"NaN" (for other not-a-numbers, like result of division by zero) | |
123 | *</ul> | |
124 | *<p> | |
125 | * Since JSON specification does not allow use of such values, | |
126 | * this is a non-standard feature, and as such disabled by default. | |
127 | */ | |
128 | @SuppressWarnings("deprecation") | |
129 | ALLOW_NON_NUMERIC_NUMBERS(false, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS), | |
130 | ||
131 | // // // Support for non-standard data format constructs: array/value separators | |
132 | ||
133 | /** | |
134 | * Feature allows the support for "missing" values in a JSON array: missing | |
135 | * value meaning sequence of two commas, without value in-between but only | |
136 | * optional white space. | |
137 | * Enabling this feature will expose "missing" values as {@link JsonToken#VALUE_NULL} | |
138 | * tokens, which typically become Java nulls in arrays and {@link java.util.Collection} | |
139 | * in data-binding. | |
140 | * <p> | |
141 | * For example, enabling this feature will represent a JSON array <code>["value1",,"value3",]</code> | |
142 | * as <code>["value1", null, "value3", null]</code> | |
143 | * <p> | |
144 | * Since the JSON specification does not allow missing values this is a non-compliant JSON | |
145 | * feature and is disabled by default. | |
146 | */ | |
147 | @SuppressWarnings("deprecation") | |
148 | ALLOW_MISSING_VALUES(false, JsonParser.Feature.ALLOW_MISSING_VALUES), | |
149 | ||
150 | /** | |
151 | * Feature that determines whether {@link JsonParser} will allow for a single trailing | |
152 | * comma following the final value (in an Array) or member (in an Object). These commas | |
153 | * will simply be ignored. | |
154 | * <p> | |
155 | * For example, when this feature is enabled, <code>[true,true,]</code> is equivalent to | |
156 | * <code>[true, true]</code> and <code>{"a": true,}</code> is equivalent to | |
157 | * <code>{"a": true}</code>. | |
158 | * <p> | |
159 | * When combined with <code>ALLOW_MISSING_VALUES</code>, this feature takes priority, and | |
160 | * the final trailing comma in an array declaration does not imply a missing | |
161 | * (<code>null</code>) value. For example, when both <code>ALLOW_MISSING_VALUES</code> | |
162 | * and <code>ALLOW_TRAILING_COMMA</code> are enabled, <code>[true,true,]</code> is | |
163 | * equivalent to <code>[true, true]</code>, and <code>[true,true,,]</code> is equivalent to | |
164 | * <code>[true, true, null]</code>. | |
165 | * <p> | |
166 | * Since the JSON specification does not permit trailing commas, this is a non-standard | |
167 | * feature, and as such disabled by default. | |
168 | */ | |
169 | @SuppressWarnings("deprecation") | |
170 | ALLOW_TRAILING_COMMA(false, JsonParser.Feature.ALLOW_TRAILING_COMMA), | |
171 | ; | |
172 | ||
173 | final private boolean _defaultState; | |
174 | final private int _mask; | |
175 | ||
176 | /** | |
177 | * For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s; | |
178 | * if so, this is the feature to enable/disable. | |
179 | */ | |
180 | final private JsonParser.Feature _mappedFeature; | |
181 | ||
182 | /** | |
183 | * Method that calculates bit set (flags) of all features that | |
184 | * are enabled by default. | |
185 | */ | |
186 | public static int collectDefaults() | |
187 | { | |
188 | int flags = 0; | |
189 | for (JsonReadFeature f : values()) { | |
190 | if (f.enabledByDefault()) { | |
191 | flags |= f.getMask(); | |
192 | } | |
193 | } | |
194 | return flags; | |
195 | } | |
196 | ||
197 | private JsonReadFeature(boolean defaultState, | |
198 | JsonParser.Feature mapTo) { | |
199 | _defaultState = defaultState; | |
200 | _mask = (1 << ordinal()); | |
201 | _mappedFeature = mapTo; | |
202 | } | |
203 | ||
204 | @Override | |
205 | public boolean enabledByDefault() { return _defaultState; } | |
206 | @Override | |
207 | public int getMask() { return _mask; } | |
208 | @Override | |
209 | public boolean enabledIn(int flags) { return (flags & _mask) != 0; } | |
210 | ||
211 | public JsonParser.Feature mappedFeature() { return _mappedFeature; } | |
212 | } |
28 | 28 | |
29 | 29 | /* |
30 | 30 | /********************************************************** |
31 | /* Simple instance reuse slots; speed up things | |
32 | /* a bit (10-15%) for docs with lots of small | |
33 | /* arrays/objects | |
31 | /* Simple instance reuse slots; speed up things a bit (10-15%) | |
32 | /* for docs with lots of small arrays/objects | |
34 | 33 | /********************************************************** |
35 | 34 | */ |
36 | 35 | |
71 | 70 | _parent = parent; |
72 | 71 | _dups = dups; |
73 | 72 | _index = -1; |
73 | } | |
74 | ||
75 | /* @since 2.10 */ | |
76 | protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups, | |
77 | Object currValue) { | |
78 | super(); | |
79 | _type = type; | |
80 | _parent = parent; | |
81 | _dups = dups; | |
82 | _index = -1; | |
83 | _currentValue = currValue; | |
74 | 84 | } |
75 | 85 | |
76 | 86 | protected JsonWriteContext reset(int type) { |
83 | 93 | return this; |
84 | 94 | } |
85 | 95 | |
96 | /* @since 2.10 */ | |
97 | protected JsonWriteContext reset(int type, Object currValue) { | |
98 | _type = type; | |
99 | _index = -1; | |
100 | _currentName = null; | |
101 | _gotName = false; | |
102 | _currentValue = currValue; | |
103 | if (_dups != null) { _dups.reset(); } | |
104 | return this; | |
105 | } | |
106 | ||
86 | 107 | public JsonWriteContext withDupDetector(DupDetector dups) { |
87 | 108 | _dups = dups; |
88 | 109 | return this; |
117 | 138 | public JsonWriteContext createChildArrayContext() { |
118 | 139 | JsonWriteContext ctxt = _child; |
119 | 140 | if (ctxt == null) { |
120 | _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, (_dups == null) ? null : _dups.child()); | |
141 | _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, | |
142 | (_dups == null) ? null : _dups.child()); | |
121 | 143 | return ctxt; |
122 | 144 | } |
123 | 145 | return ctxt.reset(TYPE_ARRAY); |
124 | 146 | } |
125 | 147 | |
148 | /* @since 2.10 */ | |
149 | public JsonWriteContext createChildArrayContext(Object currValue) { | |
150 | JsonWriteContext ctxt = _child; | |
151 | if (ctxt == null) { | |
152 | _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, | |
153 | (_dups == null) ? null : _dups.child(), currValue); | |
154 | return ctxt; | |
155 | } | |
156 | return ctxt.reset(TYPE_ARRAY, currValue); | |
157 | } | |
158 | ||
126 | 159 | public JsonWriteContext createChildObjectContext() { |
127 | 160 | JsonWriteContext ctxt = _child; |
128 | 161 | if (ctxt == null) { |
129 | _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, (_dups == null) ? null : _dups.child()); | |
162 | _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, | |
163 | (_dups == null) ? null : _dups.child()); | |
130 | 164 | return ctxt; |
131 | 165 | } |
132 | 166 | return ctxt.reset(TYPE_OBJECT); |
167 | } | |
168 | ||
169 | /* @since 2.10 */ | |
170 | public JsonWriteContext createChildObjectContext(Object currValue) { | |
171 | JsonWriteContext ctxt = _child; | |
172 | if (ctxt == null) { | |
173 | _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, | |
174 | (_dups == null) ? null : _dups.child(), currValue); | |
175 | return ctxt; | |
176 | } | |
177 | return ctxt.reset(TYPE_OBJECT, currValue); | |
133 | 178 | } |
134 | 179 | |
135 | 180 | @Override public final JsonWriteContext getParent() { return _parent; } |
152 | 197 | // could also clear the current name, but seems cheap enough to leave? |
153 | 198 | return _parent; |
154 | 199 | } |
155 | ||
200 | ||
156 | 201 | public DupDetector getDupDetector() { |
157 | 202 | return _dups; |
158 | 203 | } |
0 | package com.fasterxml.jackson.core.json; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | /** | |
5 | * Token writer features specific to JSON backend. | |
6 | * | |
7 | * @since 2.10 | |
8 | */ | |
9 | public enum JsonWriteFeature | |
10 | implements FormatFeature | |
11 | { | |
12 | // // // Support for non-standard data format constructs: comments | |
13 | ||
14 | // // Quoting/ecsaping-related features | |
15 | ||
16 | /** | |
17 | * Feature that determines whether JSON Object field names are | |
18 | * quoted using double-quotes, as specified by JSON specification | |
19 | * or not. Ability to disable quoting was added to support use | |
20 | * cases where they are not usually expected, which most commonly | |
21 | * occurs when used straight from Javascript. | |
22 | *<p> | |
23 | * Feature is enabled by default (since it is required by JSON specification). | |
24 | */ | |
25 | @SuppressWarnings("deprecation") | |
26 | QUOTE_FIELD_NAMES(true, JsonGenerator.Feature.QUOTE_FIELD_NAMES), | |
27 | ||
28 | /** | |
29 | * Feature that determines whether "NaN" ("not a number", that is, not | |
30 | * real number) float/double values are output as JSON strings. | |
31 | * The values checked are Double.Nan, | |
32 | * Double.POSITIVE_INFINITY and Double.NEGATIVE_INIFINTY (and | |
33 | * associated Float values). | |
34 | * If feature is disabled, these numbers are still output using | |
35 | * associated literal values, resulting in non-conforming | |
36 | * output. | |
37 | *<p> | |
38 | * Feature is enabled by default. | |
39 | */ | |
40 | @SuppressWarnings("deprecation") | |
41 | WRITE_NAN_AS_STRINGS(true, JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS), | |
42 | ||
43 | /** | |
44 | * Feature that forces all regular number values to be written as JSON Strings, | |
45 | * instead of as JSON Numbers. | |
46 | * Default state is 'false', meaning that Java numbers are to | |
47 | * be serialized using basic numeric representation but | |
48 | * if enabled all such numeric values are instead written out as | |
49 | * JSON Strings instead. | |
50 | *<p> | |
51 | * One use case is to avoid problems with Javascript limitations: | |
52 | * since Javascript standard specifies that all number handling | |
53 | * should be done using 64-bit IEEE 754 floating point values, | |
54 | * result being that some 64-bit integer values can not be | |
55 | * accurately represent (as mantissa is only 51 bit wide). | |
56 | *<p> | |
57 | * Feature is disabled by default. | |
58 | */ | |
59 | @SuppressWarnings("deprecation") | |
60 | WRITE_NUMBERS_AS_STRINGS(false, JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS), | |
61 | ||
62 | /** | |
63 | * Feature that specifies that all characters beyond 7-bit ASCII | |
64 | * range (i.e. code points of 128 and above) need to be output | |
65 | * using format-specific escapes (for JSON, backslash escapes), | |
66 | * if format uses escaping mechanisms (which is generally true | |
67 | * for textual formats but not for binary formats). | |
68 | *<p> | |
69 | * Note that this setting may not necessarily make sense for all | |
70 | * data formats (for example, binary formats typically do not use | |
71 | * any escaping mechanisms; and some textual formats do not have | |
72 | * general-purpose escaping); if so, settings is simply ignored. | |
73 | * Put another way, effects of this feature are data-format specific. | |
74 | *<p> | |
75 | * Feature is disabled by default. | |
76 | */ | |
77 | @SuppressWarnings("deprecation") | |
78 | ESCAPE_NON_ASCII(false, JsonGenerator.Feature.ESCAPE_NON_ASCII), | |
79 | ||
80 | //23-Nov-2015, tatu: for [core#223], if and when it gets implemented | |
81 | /* | |
82 | * Feature that specifies handling of UTF-8 content that contains | |
83 | * characters beyond BMP (Basic Multilingual Plane), which are | |
84 | * represented in UCS-2 (Java internal character encoding) as two | |
85 | * "surrogate" characters. If feature is enabled, these surrogate | |
86 | * pairs are separately escaped using backslash escapes; if disabled, | |
87 | * native output (4-byte UTF-8 sequence, or, with char-backed output | |
88 | * targets, writing of surrogates as is which is typically converted | |
89 | * by {@link java.io.Writer} into 4-byte UTF-8 sequence eventually) | |
90 | * is used. | |
91 | *<p> | |
92 | * Note that the original JSON specification suggests use of escaping; | |
93 | * but that this is not correct from standard UTF-8 handling perspective. | |
94 | * Because of two competing goals, this feature was added to allow either | |
95 | * behavior to be used, but defaulting to UTF-8 specification compliant | |
96 | * mode. | |
97 | *<p> | |
98 | * Feature is disabled by default. | |
99 | */ | |
100 | // ESCAPE_UTF8_SURROGATES(false, JsonGenerator.Feature.ESCAPE_UTF8_SURROGATES), | |
101 | ||
102 | ; | |
103 | ||
104 | final private boolean _defaultState; | |
105 | final private int _mask; | |
106 | ||
107 | /** | |
108 | * For backwards compatibility we may need to map to one of existing {@link JsonGenerator.Feature}s; | |
109 | * if so, this is the feature to enable/disable. | |
110 | */ | |
111 | final private JsonGenerator.Feature _mappedFeature; | |
112 | ||
113 | /** | |
114 | * Method that calculates bit set (flags) of all features that | |
115 | * are enabled by default. | |
116 | */ | |
117 | public static int collectDefaults() | |
118 | { | |
119 | int flags = 0; | |
120 | for (JsonWriteFeature f : values()) { | |
121 | if (f.enabledByDefault()) { | |
122 | flags |= f.getMask(); | |
123 | } | |
124 | } | |
125 | return flags; | |
126 | } | |
127 | ||
128 | private JsonWriteFeature(boolean defaultState, | |
129 | JsonGenerator.Feature mapTo) { | |
130 | _defaultState = defaultState; | |
131 | _mask = (1 << ordinal()); | |
132 | _mappedFeature = mapTo; | |
133 | } | |
134 | ||
135 | @Override | |
136 | public boolean enabledByDefault() { return _defaultState; } | |
137 | @Override | |
138 | public int getMask() { return _mask; } | |
139 | @Override | |
140 | public boolean enabledIn(int flags) { return (flags & _mask) != 0; } | |
141 | ||
142 | public JsonGenerator.Feature mappedFeature() { return _mappedFeature; } | |
143 | } |
18 | 18 | public class ReaderBasedJsonParser // final in 2.3, earlier |
19 | 19 | extends ParserBase |
20 | 20 | { |
21 | protected final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); | |
21 | @SuppressWarnings("deprecation") | |
22 | private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); | |
23 | ||
24 | @SuppressWarnings("deprecation") | |
25 | private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); | |
26 | ||
27 | @SuppressWarnings("deprecation") | |
28 | private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask(); | |
29 | ||
30 | @SuppressWarnings("deprecation") | |
31 | private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); | |
32 | private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); | |
33 | private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); | |
34 | ||
35 | private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); | |
36 | private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); | |
22 | 37 | |
23 | 38 | // Latin1 encoding is not supported, but we do use 8-bit subset for |
24 | 39 | // pre-processing task, to simplify first pass, keep it fast. |
1124 | 1139 | */ |
1125 | 1140 | case ',': |
1126 | 1141 | case ']': |
1127 | if(isEnabled(Feature.ALLOW_MISSING_VALUES)) { | |
1128 | _inputPtr--; | |
1129 | return (_currToken = JsonToken.VALUE_NULL); | |
1130 | } | |
1142 | if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { | |
1143 | --_inputPtr; | |
1144 | return (_currToken = JsonToken.VALUE_NULL); | |
1145 | } | |
1131 | 1146 | } |
1132 | 1147 | return (_currToken = _handleOddValue(i)); |
1133 | 1148 | } |
1134 | ||
1135 | 1149 | // note: identical to one in UTF8StreamJsonParser |
1136 | 1150 | @Override |
1137 | 1151 | public final String nextTextValue() throws IOException |
1584 | 1598 | if (ch < '0' || ch > '9') { |
1585 | 1599 | return '0'; |
1586 | 1600 | } |
1587 | if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { | |
1601 | if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { | |
1588 | 1602 | reportInvalidNumber("Leading zeroes not allowed"); |
1589 | 1603 | } |
1590 | 1604 | // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number) |
1620 | 1634 | if (ch == 'N') { |
1621 | 1635 | String match = negative ? "-INF" :"+INF"; |
1622 | 1636 | _matchToken(match, 3); |
1623 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
1637 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
1624 | 1638 | return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); |
1625 | 1639 | } |
1626 | 1640 | _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
1627 | 1641 | } else if (ch == 'n') { |
1628 | 1642 | String match = negative ? "-Infinity" :"+Infinity"; |
1629 | 1643 | _matchToken(match, 3); |
1630 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
1644 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
1631 | 1645 | return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); |
1632 | 1646 | } |
1633 | 1647 | _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
1758 | 1772 | protected String _handleOddName(int i) throws IOException |
1759 | 1773 | { |
1760 | 1774 | // [JACKSON-173]: allow single quotes |
1761 | if (i == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
1775 | if (i == '\'' && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
1762 | 1776 | return _parseAposName(); |
1763 | 1777 | } |
1764 | 1778 | // [JACKSON-69]: allow unquoted names if feature enabled: |
1765 | if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { | |
1779 | if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { | |
1766 | 1780 | _reportUnexpectedChar(i, "was expecting double-quote to start field name"); |
1767 | 1781 | } |
1768 | 1782 | final int[] codes = CharTypes.getInputCodeLatin1JsNames(); |
1852 | 1866 | * Also, no separation to fast/slow parsing; we'll just do |
1853 | 1867 | * one regular (~= slowish) parsing, to keep code simple |
1854 | 1868 | */ |
1855 | if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
1869 | if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
1856 | 1870 | return _handleApos(); |
1857 | 1871 | } |
1858 | 1872 | break; |
1866 | 1880 | } |
1867 | 1881 | // fall through |
1868 | 1882 | case ',': |
1869 | if (isEnabled(Feature.ALLOW_MISSING_VALUES)) { | |
1883 | if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { | |
1870 | 1884 | --_inputPtr; |
1871 | 1885 | return JsonToken.VALUE_NULL; |
1872 | 1886 | } |
1873 | 1887 | break; |
1874 | 1888 | case 'N': |
1875 | 1889 | _matchToken("NaN", 1); |
1876 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
1890 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
1877 | 1891 | return resetAsNaN("NaN", Double.NaN); |
1878 | 1892 | } |
1879 | 1893 | _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
1880 | 1894 | break; |
1881 | 1895 | case 'I': |
1882 | 1896 | _matchToken("Infinity", 1); |
1883 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
1897 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
1884 | 1898 | return resetAsNaN("Infinity", Double.POSITIVE_INFINITY); |
1885 | 1899 | } |
1886 | 1900 | _reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
1895 | 1909 | } |
1896 | 1910 | // [core#77] Try to decode most likely token |
1897 | 1911 | if (Character.isJavaIdentifierStart(i)) { |
1898 | _reportInvalidToken(""+((char) i), "('true', 'false' or 'null')"); | |
1912 | _reportInvalidToken(""+((char) i), _validJsonTokenList()); | |
1899 | 1913 | } |
1900 | 1914 | // but if it doesn't look like a token: |
1901 | _reportUnexpectedChar(i, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); | |
1915 | _reportUnexpectedChar(i, "expected a valid value "+_validJsonValueList()); | |
1902 | 1916 | return null; |
1903 | 1917 | } |
1904 | 1918 | |
2416 | 2430 | |
2417 | 2431 | private void _skipComment() throws IOException |
2418 | 2432 | { |
2419 | if (!isEnabled(Feature.ALLOW_COMMENTS)) { | |
2433 | if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { | |
2420 | 2434 | _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); |
2421 | 2435 | } |
2422 | 2436 | // First: check which comment (if either) it is: |
2466 | 2480 | |
2467 | 2481 | private boolean _skipYAMLComment() throws IOException |
2468 | 2482 | { |
2469 | if (!isEnabled(Feature.ALLOW_YAML_COMMENTS)) { | |
2483 | if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { | |
2470 | 2484 | return false; |
2471 | 2485 | } |
2472 | 2486 | _skipLine(); |
2827 | 2841 | */ |
2828 | 2842 | |
2829 | 2843 | protected void _reportInvalidToken(String matchedPart) throws IOException { |
2830 | _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN"); | |
2844 | _reportInvalidToken(matchedPart, _validJsonTokenList()); | |
2831 | 2845 | } |
2832 | 2846 | |
2833 | 2847 | protected void _reportInvalidToken(String matchedPart, String msg) throws IOException |
36 | 36 | { |
37 | 37 | final static byte BYTE_LF = (byte) '\n'; |
38 | 38 | |
39 | @SuppressWarnings("deprecation") | |
40 | private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); | |
41 | @SuppressWarnings("deprecation") | |
42 | private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); | |
43 | @SuppressWarnings("deprecation") | |
44 | private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask(); | |
45 | @SuppressWarnings("deprecation") | |
46 | private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); | |
47 | private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); | |
48 | private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); | |
49 | private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); | |
50 | private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); | |
51 | ||
39 | 52 | // This is the main input-code lookup table, fetched eagerly |
40 | 53 | private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8(); |
41 | 54 | |
604 | 617 | i = _skipWS(); |
605 | 618 | |
606 | 619 | // Was that a trailing comma? |
607 | if (Feature.ALLOW_TRAILING_COMMA.enabledIn(_features)) { | |
620 | if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { | |
608 | 621 | if (i == INT_RBRACKET || i == INT_RCURLY) { |
609 | 622 | _closeScope(i); |
610 | 623 | return _currToken; |
786 | 799 | i = _skipWS(); |
787 | 800 | |
788 | 801 | // Was that a trailing comma? |
789 | if (Feature.ALLOW_TRAILING_COMMA.enabledIn(_features)) { | |
802 | if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { | |
790 | 803 | if (i == INT_RBRACKET || i == INT_RCURLY) { |
791 | 804 | _closeScope(i); |
792 | 805 | return null; |
1078 | 1091 | return ch; |
1079 | 1092 | } |
1080 | 1093 | // we may want to allow leading zeroes them, after all... |
1081 | if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { | |
1094 | if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { | |
1082 | 1095 | reportInvalidNumber("Leading zeroes not allowed"); |
1083 | 1096 | } |
1084 | 1097 | // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number) |
1494 | 1507 | */ |
1495 | 1508 | protected String _handleOddName(int ch) throws IOException |
1496 | 1509 | { |
1497 | if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
1510 | if (ch == '\'' && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
1498 | 1511 | return _parseAposName(); |
1499 | 1512 | } |
1500 | if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { | |
1513 | if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { | |
1501 | 1514 | char c = (char) _decodeCharForError(ch); |
1502 | 1515 | _reportUnexpectedChar(c, "was expecting double-quote to start field name"); |
1503 | 1516 | } |
2013 | 2026 | /* !!! TODO: 08-May-2016, tatu: To support `Feature.ALLOW_MISSING_VALUES` would |
2014 | 2027 | * need handling here... |
2015 | 2028 | */ |
2016 | if (isEnabled(Feature.ALLOW_MISSING_VALUES)) { | |
2029 | if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { | |
2017 | 2030 | // _inputPtr--; |
2018 | 2031 | _nextByte = c; |
2019 | 2032 | return JsonToken.VALUE_NULL; |
2024 | 2037 | // been handled earlier |
2025 | 2038 | _reportUnexpectedChar(c, "expected a value"); |
2026 | 2039 | case '\'': |
2027 | if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
2040 | if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
2028 | 2041 | return _handleApos(); |
2029 | 2042 | } |
2030 | 2043 | break; |
2031 | 2044 | case 'N': |
2032 | 2045 | _matchToken("NaN", 1); |
2033 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
2046 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
2034 | 2047 | return resetAsNaN("NaN", Double.NaN); |
2035 | 2048 | } |
2036 | 2049 | _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
2037 | 2050 | break; |
2038 | 2051 | case 'I': |
2039 | 2052 | _matchToken("Infinity", 1); |
2040 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
2053 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
2041 | 2054 | return resetAsNaN("Infinity", Double.POSITIVE_INFINITY); |
2042 | 2055 | } |
2043 | 2056 | _reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
2047 | 2060 | } |
2048 | 2061 | // [core#77] Try to decode most likely token |
2049 | 2062 | if (Character.isJavaIdentifierStart(c)) { |
2050 | _reportInvalidToken(c, ""+((char) c), "('true', 'false' or 'null')"); | |
2063 | _reportInvalidToken(c, ""+((char) c), _validJsonTokenList()); | |
2051 | 2064 | } |
2052 | 2065 | // but if it doesn't look like a token: |
2053 | _reportUnexpectedChar(c, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); | |
2066 | _reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList()); | |
2054 | 2067 | return null; |
2055 | 2068 | } |
2056 | 2069 | |
2145 | 2158 | break; |
2146 | 2159 | } |
2147 | 2160 | _matchToken(match, 3); |
2148 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
2161 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
2149 | 2162 | return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); |
2150 | 2163 | } |
2151 | 2164 | _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
2363 | 2376 | |
2364 | 2377 | private final void _skipComment() throws IOException |
2365 | 2378 | { |
2366 | if (!isEnabled(Feature.ALLOW_COMMENTS)) { | |
2379 | if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { | |
2367 | 2380 | _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); |
2368 | 2381 | } |
2369 | 2382 | int c = _inputData.readUnsignedByte(); |
2418 | 2431 | |
2419 | 2432 | private final boolean _skipYAMLComment() throws IOException |
2420 | 2433 | { |
2421 | if (!isEnabled(Feature.ALLOW_YAML_COMMENTS)) { | |
2434 | if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { | |
2422 | 2435 | return false; |
2423 | 2436 | } |
2424 | 2437 | _skipLine(); |
2659 | 2672 | |
2660 | 2673 | protected void _reportInvalidToken(int ch, String matchedPart) throws IOException |
2661 | 2674 | { |
2662 | _reportInvalidToken(ch, matchedPart, "'null', 'true', 'false' or NaN"); | |
2675 | _reportInvalidToken(ch, matchedPart, _validJsonTokenList()); | |
2663 | 2676 | } |
2664 | 2677 | |
2665 | 2678 | protected void _reportInvalidToken(int ch, String matchedPart, String msg) |
51 | 51 | * |
52 | 52 | * @since 2.8 |
53 | 53 | */ |
54 | protected byte _quoteChar = '"'; // TODO: make configurable | |
55 | ||
54 | protected byte _quoteChar; | |
55 | ||
56 | 56 | /* |
57 | 57 | /********************************************************** |
58 | 58 | /* Output buffering |
112 | 112 | /********************************************************** |
113 | 113 | */ |
114 | 114 | |
115 | /** | |
116 | * @since 2.10 | |
117 | */ | |
118 | @SuppressWarnings("deprecation") | |
115 | 119 | public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, |
116 | OutputStream out) | |
120 | OutputStream out, char quoteChar) | |
117 | 121 | { |
118 | 122 | super(ctxt, features, codec); |
119 | 123 | _outputStream = out; |
124 | _quoteChar = (byte) quoteChar; | |
125 | if (quoteChar != '"') { // since 2.10 | |
126 | _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar); | |
127 | } | |
128 | ||
120 | 129 | _bufferRecyclable = true; |
121 | 130 | _outputBuffer = ctxt.allocWriteEncodingBuffer(); |
122 | 131 | _outputEnd = _outputBuffer.length; |
134 | 143 | setHighestNonEscapedChar(127); |
135 | 144 | } |
136 | 145 | } |
137 | ||
146 | ||
147 | /** | |
148 | * @since 2.10 | |
149 | */ | |
138 | 150 | public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, |
139 | OutputStream out, | |
151 | OutputStream out, char quoteChar, | |
140 | 152 | byte[] outputBuffer, int outputOffset, boolean bufferRecyclable) |
141 | 153 | { |
142 | 154 | |
143 | 155 | super(ctxt, features, codec); |
144 | 156 | _outputStream = out; |
157 | _quoteChar = (byte) quoteChar; | |
158 | if (quoteChar != '"') { // since 2.10 | |
159 | _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar); | |
160 | } | |
161 | ||
145 | 162 | _bufferRecyclable = bufferRecyclable; |
146 | 163 | _outputTail = outputOffset; |
147 | 164 | _outputBuffer = outputBuffer; |
152 | 169 | _charBufferLength = _charBuffer.length; |
153 | 170 | } |
154 | 171 | |
172 | @Deprecated // since 2.10 | |
173 | public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, | |
174 | OutputStream out) { | |
175 | this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR); | |
176 | } | |
177 | ||
178 | @Deprecated // since 2.10 | |
179 | public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, | |
180 | OutputStream out, | |
181 | byte[] outputBuffer, int outputOffset, boolean bufferRecyclable) | |
182 | { | |
183 | this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR, | |
184 | outputBuffer, outputOffset, bufferRecyclable); | |
185 | } | |
186 | ||
155 | 187 | /* |
156 | 188 | /********************************************************** |
157 | 189 | /* Overridden configuration methods |
158 | 190 | /********************************************************** |
159 | 191 | */ |
160 | ||
192 | ||
161 | 193 | @Override |
162 | 194 | public Object getOutputTarget() { |
163 | 195 | return _outputStream; |
292 | 324 | } |
293 | 325 | } |
294 | 326 | |
327 | @Override // since 2.10 | |
328 | public void writeStartArray(int size) throws IOException | |
329 | { | |
330 | _verifyValueWrite("start an array"); | |
331 | _writeContext = _writeContext.createChildArrayContext(); | |
332 | if (_cfgPrettyPrinter != null) { | |
333 | _cfgPrettyPrinter.writeStartArray(this); | |
334 | } else { | |
335 | if (_outputTail >= _outputEnd) { | |
336 | _flushBuffer(); | |
337 | } | |
338 | _outputBuffer[_outputTail++] = BYTE_LBRACKET; | |
339 | } | |
340 | } | |
341 | ||
295 | 342 | @Override |
296 | 343 | public final void writeEndArray() throws IOException |
297 | 344 | { |
328 | 375 | public void writeStartObject(Object forValue) throws IOException |
329 | 376 | { |
330 | 377 | _verifyValueWrite("start an object"); |
331 | JsonWriteContext ctxt = _writeContext.createChildObjectContext(); | |
378 | JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue); | |
332 | 379 | _writeContext = ctxt; |
333 | if (forValue != null) { | |
334 | ctxt.setCurrentValue(forValue); | |
335 | } | |
336 | 380 | if (_cfgPrettyPrinter != null) { |
337 | 381 | _cfgPrettyPrinter.writeStartObject(this); |
338 | 382 | } else { |
342 | 386 | _outputBuffer[_outputTail++] = '{'; |
343 | 387 | } |
344 | 388 | } |
345 | ||
389 | ||
346 | 390 | @Override |
347 | 391 | public final void writeEndObject() throws IOException |
348 | 392 | { |
423 | 467 | } |
424 | 468 | _outputBuffer[_outputTail++] = _quoteChar; |
425 | 469 | } |
426 | _writeBytes(name.asQuotedUTF8()); | |
470 | int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); | |
471 | if (len < 0) { | |
472 | _writeBytes(name.asQuotedUTF8()); | |
473 | } else { | |
474 | _outputTail += len; | |
475 | } | |
427 | 476 | if (addQuotes) { |
428 | 477 | if (_outputTail >= _outputEnd) { |
429 | 478 | _flushBuffer(); |
474 | 523 | |
475 | 524 | final char[] buf = _charBuffer; |
476 | 525 | |
477 | //Add leading quote | |
478 | if ((_outputTail + len) >= _outputEnd) { | |
479 | _flushBuffer(); | |
480 | } | |
481 | _outputBuffer[_outputTail++] = _quoteChar; | |
482 | ||
483 | //read | |
526 | // Add leading quote | |
527 | if (_outputTail >= _outputEnd) { | |
528 | _flushBuffer(); | |
529 | } | |
530 | _outputBuffer[_outputTail++] = _quoteChar; | |
531 | ||
532 | // read | |
484 | 533 | while (toRead > 0){ |
485 | 534 | int toReadNow = Math.min(toRead, buf.length); |
486 | 535 | int numRead = reader.read(buf, 0, toReadNow); |
495 | 544 | toRead -= numRead; |
496 | 545 | } |
497 | 546 | |
498 | //Add trailing quote | |
499 | if ((_outputTail + len) >= _outputEnd) { | |
547 | // Add trailing quote | |
548 | if (_outputTail >= _outputEnd) { | |
500 | 549 | _flushBuffer(); |
501 | 550 | } |
502 | 551 | _outputBuffer[_outputTail++] = _quoteChar; |
650 | 699 | @Override |
651 | 700 | public void writeRaw(SerializableString text) throws IOException |
652 | 701 | { |
653 | byte[] raw = text.asUnquotedUTF8(); | |
654 | if (raw.length > 0) { | |
655 | _writeBytes(raw); | |
702 | int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail); | |
703 | if (len < 0) { | |
704 | _writeBytes(text.asUnquotedUTF8()); | |
705 | } else { | |
706 | _outputTail += len; | |
656 | 707 | } |
657 | 708 | } |
658 | 709 | |
660 | 711 | @Override |
661 | 712 | public void writeRawValue(SerializableString text) throws IOException { |
662 | 713 | _verifyValueWrite(WRITE_RAW); |
663 | byte[] raw = text.asUnquotedUTF8(); | |
664 | if (raw.length > 0) { | |
665 | _writeBytes(raw); | |
714 | int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail); | |
715 | if (len < 0) { | |
716 | _writeBytes(text.asUnquotedUTF8()); | |
717 | } else { | |
718 | _outputTail += len; | |
666 | 719 | } |
667 | 720 | } |
668 | 721 | |
952 | 1005 | } |
953 | 1006 | } |
954 | 1007 | |
955 | ||
1008 | @SuppressWarnings("deprecation") | |
956 | 1009 | @Override |
957 | 1010 | public void writeNumber(double d) throws IOException |
958 | 1011 | { |
959 | 1012 | if (_cfgNumbersAsStrings || |
960 | (((Double.isNaN(d) || Double.isInfinite(d)) | |
961 | && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features)))) { | |
1013 | (NumberOutput.notFinite(d) | |
1014 | && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) { | |
962 | 1015 | writeString(String.valueOf(d)); |
963 | 1016 | return; |
964 | 1017 | } |
967 | 1020 | writeRaw(String.valueOf(d)); |
968 | 1021 | } |
969 | 1022 | |
1023 | @SuppressWarnings("deprecation") | |
970 | 1024 | @Override |
971 | 1025 | public void writeNumber(float f) throws IOException |
972 | 1026 | { |
973 | 1027 | if (_cfgNumbersAsStrings || |
974 | // [JACKSON-139] | |
975 | (((Float.isNaN(f) || Float.isInfinite(f)) | |
976 | && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features)))) { | |
1028 | (NumberOutput.notFinite(f) | |
1029 | && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) { | |
977 | 1030 | writeString(String.valueOf(f)); |
978 | 1031 | return; |
979 | 1032 | } |
1306 | 1359 | } |
1307 | 1360 | _outputTail = outputPtr; |
1308 | 1361 | if (offset < len) { |
1309 | // [JACKSON-106] | |
1310 | 1362 | if (_characterEscapes != null) { |
1311 | 1363 | _writeCustomStringSegment2(cbuf, offset, len); |
1312 | // [JACKSON-102] | |
1313 | 1364 | } else if (_maximumNonEscapedChar == 0) { |
1314 | 1365 | _writeStringSegment2(cbuf, offset, len); |
1315 | 1366 | } else { |
19 | 19 | { |
20 | 20 | final static byte BYTE_LF = (byte) '\n'; |
21 | 21 | |
22 | @SuppressWarnings("deprecation") | |
23 | private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); | |
24 | @SuppressWarnings("deprecation") | |
25 | private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); | |
26 | @SuppressWarnings("deprecation") | |
27 | private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask(); | |
28 | @SuppressWarnings("deprecation") | |
29 | private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); | |
30 | private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); | |
31 | private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); | |
32 | private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); | |
33 | private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); | |
34 | ||
22 | 35 | // This is the main input-code lookup table, fetched eagerly |
23 | 36 | private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8(); |
24 | 37 | |
25 | 38 | // Latin1 encoding is not supported, but we do use 8-bit subset for |
26 | 39 | // pre-processing task, to simplify first pass, keep it fast. |
27 | 40 | protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1(); |
28 | ||
29 | protected final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); | |
30 | 41 | |
31 | 42 | /* |
32 | 43 | /********************************************************** |
125 | 136 | /********************************************************** |
126 | 137 | */ |
127 | 138 | |
139 | /** | |
140 | * @deprecated Since 2.10 | |
141 | */ | |
142 | @Deprecated | |
128 | 143 | public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in, |
129 | 144 | ObjectCodec codec, ByteQuadsCanonicalizer sym, |
130 | 145 | byte[] inputBuffer, int start, int end, |
146 | boolean bufferRecyclable) | |
147 | { | |
148 | this(ctxt, features, in, codec, sym, | |
149 | inputBuffer, start, end, 0, bufferRecyclable); | |
150 | } | |
151 | ||
152 | public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in, | |
153 | ObjectCodec codec, ByteQuadsCanonicalizer sym, | |
154 | byte[] inputBuffer, int start, int end, int bytesPreProcessed, | |
131 | 155 | boolean bufferRecyclable) |
132 | 156 | { |
133 | 157 | super(ctxt, features); |
137 | 161 | _inputBuffer = inputBuffer; |
138 | 162 | _inputPtr = start; |
139 | 163 | _inputEnd = end; |
140 | _currInputRowStart = start; | |
164 | _currInputRowStart = start - bytesPreProcessed; | |
141 | 165 | // If we have offset, need to omit that from byte offset, so: |
142 | _currInputProcessed = -start; | |
166 | _currInputProcessed = -start + bytesPreProcessed; | |
143 | 167 | _bufferRecyclable = bufferRecyclable; |
144 | 168 | } |
145 | 169 | |
1490 | 1514 | return INT_0; |
1491 | 1515 | } |
1492 | 1516 | // [JACKSON-358]: we may want to allow them, after all... |
1493 | if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { | |
1517 | if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { | |
1494 | 1518 | reportInvalidNumber("Leading zeroes not allowed"); |
1495 | 1519 | } |
1496 | 1520 | // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number) |
1986 | 2010 | protected String _handleOddName(int ch) throws IOException |
1987 | 2011 | { |
1988 | 2012 | // First: may allow single quotes |
1989 | if (ch == INT_APOS && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
2013 | if (ch == INT_APOS && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
1990 | 2014 | return _parseAposName(); |
1991 | 2015 | } |
1992 | 2016 | // Allow unquoted names if feature enabled: |
1993 | if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { | |
2017 | if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { | |
1994 | 2018 | char c = (char) _decodeCharForError(ch); |
1995 | 2019 | _reportUnexpectedChar(c, "was expecting double-quote to start field name"); |
1996 | 2020 | } |
2585 | 2609 | // 28-Mar-2016: [core#116]: If Feature.ALLOW_MISSING_VALUES is enabled |
2586 | 2610 | // we may allow "missing values", that is, encountering a trailing |
2587 | 2611 | // comma or closing marker where value would be expected |
2588 | if (isEnabled(Feature.ALLOW_MISSING_VALUES)) { | |
2612 | if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { | |
2589 | 2613 | --_inputPtr; |
2590 | 2614 | return JsonToken.VALUE_NULL; |
2591 | 2615 | } |
2595 | 2619 | // been handled earlier |
2596 | 2620 | _reportUnexpectedChar(c, "expected a value"); |
2597 | 2621 | case '\'': |
2598 | if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
2622 | if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
2599 | 2623 | return _handleApos(); |
2600 | 2624 | } |
2601 | 2625 | break; |
2602 | 2626 | case 'N': |
2603 | 2627 | _matchToken("NaN", 1); |
2604 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
2628 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
2605 | 2629 | return resetAsNaN("NaN", Double.NaN); |
2606 | 2630 | } |
2607 | 2631 | _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
2608 | 2632 | break; |
2609 | 2633 | case 'I': |
2610 | 2634 | _matchToken("Infinity", 1); |
2611 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
2635 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
2612 | 2636 | return resetAsNaN("Infinity", Double.POSITIVE_INFINITY); |
2613 | 2637 | } |
2614 | 2638 | _reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); |
2623 | 2647 | } |
2624 | 2648 | // [core#77] Try to decode most likely token |
2625 | 2649 | if (Character.isJavaIdentifierStart(c)) { |
2626 | _reportInvalidToken(""+((char) c), "('true', 'false' or 'null')"); | |
2650 | _reportInvalidToken(""+((char) c), _validJsonTokenList()); | |
2627 | 2651 | } |
2628 | 2652 | // but if it doesn't look like a token: |
2629 | _reportUnexpectedChar(c, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); | |
2653 | _reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList()); | |
2630 | 2654 | return null; |
2631 | 2655 | } |
2632 | 2656 | |
2747 | 2771 | break; |
2748 | 2772 | } |
2749 | 2773 | _matchToken(match, 3); |
2750 | if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { | |
2774 | if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { | |
2751 | 2775 | return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); |
2752 | 2776 | } |
2753 | 2777 | _reportError("Non-standard token '%s': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow", |
3099 | 3123 | |
3100 | 3124 | private final void _skipComment() throws IOException |
3101 | 3125 | { |
3102 | if (!isEnabled(Feature.ALLOW_COMMENTS)) { | |
3126 | if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { | |
3103 | 3127 | _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); |
3104 | 3128 | } |
3105 | 3129 | // First: check which comment (if either) it is: |
3164 | 3188 | |
3165 | 3189 | private final boolean _skipYAMLComment() throws IOException |
3166 | 3190 | { |
3167 | if (!isEnabled(Feature.ALLOW_YAML_COMMENTS)) { | |
3191 | if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { | |
3168 | 3192 | return false; |
3169 | 3193 | } |
3170 | 3194 | _skipLine(); |
3256 | 3280 | _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING); |
3257 | 3281 | } |
3258 | 3282 | } |
3259 | int ch = (int) _inputBuffer[_inputPtr++]; | |
3283 | int ch = _inputBuffer[_inputPtr++] & 0xFF; | |
3260 | 3284 | int digit = CharTypes.charToHex(ch); |
3261 | 3285 | if (digit < 0) { |
3262 | 3286 | _reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence"); |
3500 | 3524 | |
3501 | 3525 | protected void _reportInvalidToken(String matchedPart, int ptr) throws IOException { |
3502 | 3526 | _inputPtr = ptr; |
3503 | _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN"); | |
3527 | _reportInvalidToken(matchedPart, _validJsonTokenList()); | |
3504 | 3528 | } |
3505 | 3529 | |
3506 | 3530 | protected void _reportInvalidToken(String matchedPart) throws IOException { |
3507 | _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN"); | |
3531 | _reportInvalidToken(matchedPart, _validJsonTokenList()); | |
3508 | 3532 | } |
3509 | 3533 | |
3510 | 3534 | protected void _reportInvalidToken(String matchedPart, String msg) throws IOException |
31 | 31 | /** |
32 | 32 | * Character used for quoting JSON Object property names |
33 | 33 | * and String values. |
34 | * | |
35 | * @since 2.8 | |
36 | */ | |
37 | protected char _quoteChar = '"'; // TODO: make configurable | |
34 | */ | |
35 | protected char _quoteChar; | |
38 | 36 | |
39 | 37 | /* |
40 | 38 | /********************************************************** |
81 | 79 | * Intermediate buffer in which characters of a String are copied |
82 | 80 | * before being encoded. |
83 | 81 | * |
84 | * @since 2.9 | |
85 | */ | |
86 | protected char[] _charBuffer; | |
82 | * @since 2.10 | |
83 | */ | |
84 | protected char[] _copyBuffer; | |
87 | 85 | |
88 | 86 | /* |
89 | 87 | /********************************************************** |
91 | 89 | /********************************************************** |
92 | 90 | */ |
93 | 91 | |
92 | @Deprecated // since 2.10 | |
94 | 93 | public WriterBasedJsonGenerator(IOContext ctxt, int features, |
95 | ObjectCodec codec, Writer w) | |
94 | ObjectCodec codec, Writer w) { | |
95 | this(ctxt, features, codec, w, JsonFactory.DEFAULT_QUOTE_CHAR); | |
96 | } | |
97 | ||
98 | /** | |
99 | * @since 2.10 | |
100 | */ | |
101 | public WriterBasedJsonGenerator(IOContext ctxt, int features, | |
102 | ObjectCodec codec, Writer w, | |
103 | char quoteChar) | |
104 | ||
96 | 105 | { |
97 | 106 | super(ctxt, features, codec); |
98 | 107 | _writer = w; |
99 | 108 | _outputBuffer = ctxt.allocConcatBuffer(); |
100 | 109 | _outputEnd = _outputBuffer.length; |
110 | _quoteChar = quoteChar; | |
111 | if (quoteChar != '"') { // since 2.10 | |
112 | _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar); | |
113 | } | |
101 | 114 | } |
102 | 115 | |
103 | 116 | /* |
192 | 205 | _outputBuffer[_outputTail++] = ','; |
193 | 206 | } |
194 | 207 | // Alternate mode, in which quoting of field names disabled? |
208 | if (_cfgUnqNames) { | |
209 | final char[] ch = name.asQuotedChars(); | |
210 | writeRaw(ch, 0, ch.length); | |
211 | return; | |
212 | } | |
213 | // we know there's room for at least one more char | |
214 | _outputBuffer[_outputTail++] = _quoteChar; | |
215 | // The beef: | |
216 | ||
217 | int len = name.appendQuoted(_outputBuffer, _outputTail); | |
218 | if (len < 0) { | |
219 | _writeFieldNameTail(name); | |
220 | return; | |
221 | } | |
222 | _outputTail += len; | |
223 | if (_outputTail >= _outputEnd) { | |
224 | _flushBuffer(); | |
225 | } | |
226 | _outputBuffer[_outputTail++] = _quoteChar; | |
227 | } | |
228 | ||
229 | private final void _writeFieldNameTail(SerializableString name) throws IOException | |
230 | { | |
195 | 231 | final char[] quoted = name.asQuotedChars(); |
196 | if (_cfgUnqNames) { | |
197 | writeRaw(quoted, 0, quoted.length); | |
198 | return; | |
199 | } | |
200 | // we know there's room for at least one more char | |
201 | _outputBuffer[_outputTail++] = _quoteChar; | |
202 | // The beef: | |
203 | final int qlen = quoted.length; | |
204 | if ((_outputTail + qlen + 1) >= _outputEnd) { | |
205 | writeRaw(quoted, 0, qlen); | |
206 | // and closing quotes; need room for one more char: | |
207 | if (_outputTail >= _outputEnd) { | |
208 | _flushBuffer(); | |
209 | } | |
210 | _outputBuffer[_outputTail++] = _quoteChar; | |
211 | } else { | |
212 | System.arraycopy(quoted, 0, _outputBuffer, _outputTail, qlen); | |
213 | _outputTail += qlen; | |
214 | _outputBuffer[_outputTail++] = _quoteChar; | |
215 | } | |
216 | } | |
217 | ||
232 | writeRaw(quoted, 0, quoted.length); | |
233 | if (_outputTail >= _outputEnd) { | |
234 | _flushBuffer(); | |
235 | } | |
236 | _outputBuffer[_outputTail++] = _quoteChar; | |
237 | } | |
238 | ||
218 | 239 | /* |
219 | 240 | /********************************************************** |
220 | 241 | /* Output method implementations, structural |
236 | 257 | } |
237 | 258 | } |
238 | 259 | |
260 | @Override // since 2.10 | |
261 | public void writeStartArray(int size) throws IOException | |
262 | { | |
263 | _verifyValueWrite("start an array"); | |
264 | _writeContext = _writeContext.createChildArrayContext(); | |
265 | if (_cfgPrettyPrinter != null) { | |
266 | _cfgPrettyPrinter.writeStartArray(this); | |
267 | } else { | |
268 | if (_outputTail >= _outputEnd) { | |
269 | _flushBuffer(); | |
270 | } | |
271 | _outputBuffer[_outputTail++] = '['; | |
272 | } | |
273 | } | |
274 | ||
239 | 275 | @Override |
240 | 276 | public void writeEndArray() throws IOException |
241 | 277 | { |
253 | 289 | _writeContext = _writeContext.clearAndGetParent(); |
254 | 290 | } |
255 | 291 | |
256 | @Override // since 2.8 | |
257 | public void writeStartObject(Object forValue) throws IOException | |
258 | { | |
259 | _verifyValueWrite("start an object"); | |
260 | JsonWriteContext ctxt = _writeContext.createChildObjectContext(); | |
261 | _writeContext = ctxt; | |
262 | if (forValue != null) { | |
263 | ctxt.setCurrentValue(forValue); | |
264 | } | |
265 | if (_cfgPrettyPrinter != null) { | |
266 | _cfgPrettyPrinter.writeStartObject(this); | |
267 | } else { | |
268 | if (_outputTail >= _outputEnd) { | |
269 | _flushBuffer(); | |
270 | } | |
271 | _outputBuffer[_outputTail++] = '{'; | |
272 | } | |
273 | } | |
274 | ||
275 | 292 | @Override |
276 | 293 | public void writeStartObject() throws IOException |
277 | 294 | { |
287 | 304 | } |
288 | 305 | } |
289 | 306 | |
307 | @Override // since 2.8 | |
308 | public void writeStartObject(Object forValue) throws IOException | |
309 | { | |
310 | _verifyValueWrite("start an object"); | |
311 | JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue); | |
312 | _writeContext = ctxt; | |
313 | if (_cfgPrettyPrinter != null) { | |
314 | _cfgPrettyPrinter.writeStartObject(this); | |
315 | } else { | |
316 | if (_outputTail >= _outputEnd) { | |
317 | _flushBuffer(); | |
318 | } | |
319 | _outputBuffer[_outputTail++] = '{'; | |
320 | } | |
321 | } | |
322 | ||
290 | 323 | @Override |
291 | 324 | public void writeEndObject() throws IOException |
292 | 325 | { |
338 | 371 | } else { |
339 | 372 | _cfgPrettyPrinter.beforeObjectEntries(this); |
340 | 373 | } |
341 | ||
342 | 374 | final char[] quoted = name.asQuotedChars(); |
343 | 375 | if (_cfgUnqNames) {// non-standard, omit quotes |
344 | 376 | writeRaw(quoted, 0, quoted.length); |
388 | 420 | _reportError("null reader"); |
389 | 421 | } |
390 | 422 | int toRead = (len >= 0) ? len : Integer.MAX_VALUE; |
391 | final char[] buf = _allocateCopyBuffer(); | |
392 | 423 | //Add leading quote |
393 | 424 | if ((_outputTail + len) >= _outputEnd) { |
394 | 425 | _flushBuffer(); |
395 | 426 | } |
396 | 427 | _outputBuffer[_outputTail++] = _quoteChar; |
397 | 428 | |
429 | final char[] buf = _allocateCopyBuffer(); | |
398 | 430 | //read |
399 | 431 | while (toRead > 0) { |
400 | 432 | int toReadNow = Math.min(toRead, buf.length); |
445 | 477 | _flushBuffer(); |
446 | 478 | } |
447 | 479 | _outputBuffer[_outputTail++] = _quoteChar; |
480 | int len = sstr.appendQuoted(_outputBuffer, _outputTail); | |
481 | if (len < 0) { | |
482 | _writeString2(sstr); | |
483 | return; | |
484 | } | |
485 | _outputTail += len; | |
486 | if (_outputTail >= _outputEnd) { | |
487 | _flushBuffer(); | |
488 | } | |
489 | _outputBuffer[_outputTail++] = _quoteChar; | |
490 | } | |
491 | ||
492 | private void _writeString2(SerializableString sstr) throws IOException | |
493 | { | |
448 | 494 | // Note: copied from writeRaw: |
449 | 495 | char[] text = sstr.asQuotedChars(); |
450 | 496 | final int len = text.length; |
451 | // Only worth buffering if it's a short write? | |
452 | 497 | if (len < SHORT_WRITE) { |
453 | 498 | int room = _outputEnd - _outputTail; |
454 | 499 | if (len > room) { |
457 | 502 | System.arraycopy(text, 0, _outputBuffer, _outputTail, len); |
458 | 503 | _outputTail += len; |
459 | 504 | } else { |
460 | // Otherwise, better just pass through: | |
461 | 505 | _flushBuffer(); |
462 | 506 | _writer.write(text, 0, len); |
463 | 507 | } |
466 | 510 | } |
467 | 511 | _outputBuffer[_outputTail++] = _quoteChar; |
468 | 512 | } |
469 | ||
513 | ||
470 | 514 | @Override |
471 | 515 | public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { |
472 | 516 | // could add support for buffering if we really want it... |
527 | 571 | // @since 2.1 |
528 | 572 | @Override |
529 | 573 | public void writeRaw(SerializableString text) throws IOException { |
530 | writeRaw(text.getValue()); | |
574 | int len = text.appendUnquoted(_outputBuffer, _outputTail); | |
575 | if (len < 0) { | |
576 | writeRaw(text.getValue()); | |
577 | return; | |
578 | } | |
579 | _outputTail += len; | |
531 | 580 | } |
532 | 581 | |
533 | 582 | @Override |
733 | 782 | } |
734 | 783 | } |
735 | 784 | |
736 | ||
785 | @SuppressWarnings("deprecation") | |
737 | 786 | @Override |
738 | 787 | public void writeNumber(double d) throws IOException |
739 | 788 | { |
740 | 789 | if (_cfgNumbersAsStrings || |
741 | // [JACKSON-139] | |
742 | (isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS) && ((Double.isNaN(d) || Double.isInfinite(d))))) { | |
790 | (NumberOutput.notFinite(d) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) { | |
743 | 791 | writeString(String.valueOf(d)); |
744 | 792 | return; |
745 | 793 | } |
748 | 796 | writeRaw(String.valueOf(d)); |
749 | 797 | } |
750 | 798 | |
799 | @SuppressWarnings("deprecation") | |
751 | 800 | @Override |
752 | 801 | public void writeNumber(float f) throws IOException |
753 | 802 | { |
754 | 803 | if (_cfgNumbersAsStrings || |
755 | // [JACKSON-139] | |
756 | (isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS) && ((Float.isNaN(f) || Float.isInfinite(f))))) { | |
804 | (NumberOutput.notFinite(f) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) { | |
757 | 805 | writeString(String.valueOf(f)); |
758 | 806 | return; |
759 | 807 | } |
940 | 988 | _outputBuffer = null; |
941 | 989 | _ioContext.releaseConcatBuffer(buf); |
942 | 990 | } |
943 | buf = _charBuffer; | |
991 | buf = _copyBuffer; | |
944 | 992 | if (buf != null) { |
945 | _charBuffer = null; | |
993 | _copyBuffer = null; | |
946 | 994 | _ioContext.releaseNameCopyBuffer(buf); |
947 | 995 | } |
948 | 996 | } |
1930 | 1978 | * @since 2.9 |
1931 | 1979 | */ |
1932 | 1980 | private char[] _allocateCopyBuffer() { |
1933 | if (_charBuffer == null) { | |
1934 | _charBuffer = _ioContext.allocNameCopyBuffer(2000); | |
1935 | } | |
1936 | return _charBuffer; | |
1981 | if (_copyBuffer == null) { | |
1982 | _copyBuffer = _ioContext.allocNameCopyBuffer(2000); | |
1983 | } | |
1984 | return _copyBuffer; | |
1937 | 1985 | } |
1938 | 1986 | |
1939 | 1987 | protected void _flushBuffer() throws IOException |
14 | 14 | extends NonBlockingJsonParserBase |
15 | 15 | implements ByteArrayFeeder |
16 | 16 | { |
17 | @SuppressWarnings("deprecation") | |
18 | private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); | |
19 | @SuppressWarnings("deprecation") | |
20 | private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); | |
21 | @SuppressWarnings("deprecation") | |
22 | private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); | |
23 | private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); | |
24 | private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); | |
25 | private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); | |
26 | private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); | |
27 | ||
17 | 28 | // This is the main input-code lookup table, fetched eagerly |
18 | 29 | private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8(); |
19 | 30 | |
552 | 563 | _updateTokenLocation(); |
553 | 564 | if (ch != INT_QUOTE) { |
554 | 565 | if (ch == INT_RCURLY) { |
555 | if (JsonParser.Feature.ALLOW_TRAILING_COMMA.enabledIn(_features)) { | |
566 | if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { | |
556 | 567 | return _closeObjectScope(); |
557 | 568 | } |
558 | 569 | } |
590 | 601 | } |
591 | 602 | } |
592 | 603 | _updateTokenLocation(); |
604 | // 17-Sep-2019, tatu: [core#563] Need to call this to update index within array | |
605 | _parsingContext.expectComma(); | |
606 | ||
593 | 607 | if (ch == INT_QUOTE) { |
594 | 608 | return _startString(); |
595 | 609 | } |
624 | 638 | return _startTrueToken(); |
625 | 639 | case '[': |
626 | 640 | return _startArrayScope(); |
627 | case ']': | |
641 | case INT_RBRACKET: | |
628 | 642 | return _closeArrayScope(); |
629 | 643 | case '{': |
630 | 644 | return _startObjectScope(); |
631 | case '}': | |
645 | case INT_RCURLY: | |
632 | 646 | return _closeObjectScope(); |
633 | 647 | default: |
634 | 648 | } |
664 | 678 | } |
665 | 679 | _reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); |
666 | 680 | } |
681 | ||
682 | // 17-Sep-2019, tatu: [core#563] Need to call this to update index within array | |
683 | _parsingContext.expectComma(); | |
684 | ||
667 | 685 | int ptr = _inputPtr; |
668 | 686 | if (ptr >= _inputEnd) { |
669 | 687 | _minorState = MINOR_VALUE_WS_AFTER_COMMA; |
710 | 728 | return _startTrueToken(); |
711 | 729 | case '[': |
712 | 730 | return _startArrayScope(); |
713 | case ']': | |
731 | case INT_RBRACKET: | |
714 | 732 | // Was that a trailing comma? |
715 | if (isEnabled(Feature.ALLOW_TRAILING_COMMA)) { | |
733 | if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { | |
716 | 734 | return _closeArrayScope(); |
717 | 735 | } |
718 | 736 | break; |
719 | 737 | case '{': |
720 | 738 | return _startObjectScope(); |
721 | case '}': | |
739 | case INT_RCURLY: | |
722 | 740 | // Was that a trailing comma? |
723 | if (isEnabled(Feature.ALLOW_TRAILING_COMMA)) { | |
741 | if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { | |
724 | 742 | return _closeObjectScope(); |
725 | 743 | } |
726 | 744 | break; |
854 | 872 | return _startTrueToken(); |
855 | 873 | case '[': |
856 | 874 | return _startArrayScope(); |
857 | case ']': | |
875 | case INT_RBRACKET: | |
858 | 876 | // Was that a trailing comma? |
859 | if (isEnabled(Feature.ALLOW_TRAILING_COMMA)) { | |
877 | if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { | |
860 | 878 | return _closeArrayScope(); |
861 | 879 | } |
862 | 880 | break; |
863 | 881 | case '{': |
864 | 882 | return _startObjectScope(); |
865 | case '}': | |
883 | case INT_RCURLY: | |
866 | 884 | // Was that a trailing comma? |
867 | if (isEnabled(Feature.ALLOW_TRAILING_COMMA)) { | |
885 | if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { | |
868 | 886 | return _closeObjectScope(); |
869 | 887 | } |
870 | 888 | break; |
876 | 894 | protected JsonToken _startUnexpectedValue(boolean leadingComma, int ch) throws IOException |
877 | 895 | { |
878 | 896 | switch (ch) { |
879 | case ']': | |
897 | case INT_RBRACKET: | |
880 | 898 | if (!_parsingContext.inArray()) { |
881 | 899 | break; |
882 | 900 | } |
885 | 903 | // 28-Mar-2016: [core#116]: If Feature.ALLOW_MISSING_VALUES is enabled |
886 | 904 | // we may allow "missing values", that is, encountering a trailing |
887 | 905 | // comma or closing marker where value would be expected |
888 | if (isEnabled(Feature.ALLOW_MISSING_VALUES)) { | |
906 | if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { | |
889 | 907 | --_inputPtr; |
890 | 908 | return _valueComplete(JsonToken.VALUE_NULL); |
891 | 909 | } |
892 | 910 | // fall through |
893 | case '}': | |
911 | case INT_RCURLY: | |
894 | 912 | // Error: neither is valid at this point; valid closers have |
895 | 913 | // been handled earlier |
896 | 914 | break; |
897 | 915 | case '\'': |
898 | if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
916 | if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
899 | 917 | return _startAposString(); |
900 | 918 | } |
901 | 919 | break; |
907 | 925 | return _finishNonStdToken(NON_STD_TOKEN_INFINITY, 1); |
908 | 926 | } |
909 | 927 | // !!! TODO: maybe try to collect more information for better diagnostics |
910 | _reportUnexpectedChar(ch, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); | |
928 | _reportUnexpectedChar(ch, "expected a valid value "+_validJsonValueList()); | |
911 | 929 | return null; |
912 | 930 | } |
913 | 931 | |
942 | 960 | |
943 | 961 | private final JsonToken _startSlashComment(int fromMinorState) throws IOException |
944 | 962 | { |
945 | if (!JsonParser.Feature.ALLOW_COMMENTS.enabledIn(_features)) { | |
963 | if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { | |
946 | 964 | _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); |
947 | 965 | } |
948 | 966 | |
966 | 984 | private final JsonToken _finishHashComment(int fromMinorState) throws IOException |
967 | 985 | { |
968 | 986 | // Could by-pass this check by refactoring, but for now simplest way... |
969 | if (!JsonParser.Feature.ALLOW_YAML_COMMENTS.enabledIn(_features)) { | |
987 | if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { | |
970 | 988 | _reportUnexpectedChar('#', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_YAML_COMMENTS' not enabled for parser)"); |
971 | 989 | } |
972 | 990 | while (true) { |
1251 | 1269 | { |
1252 | 1270 | // !!! TODO: Include non-standard ones if enabled |
1253 | 1271 | _reportError("Unrecognized token '%s': was expecting %s", _textBuffer.contentsAsString(), |
1254 | "'null', 'true' or 'false'"); | |
1272 | _validJsonTokenList()); | |
1255 | 1273 | return JsonToken.NOT_AVAILABLE; // never gets here |
1256 | 1274 | } |
1257 | ||
1275 | ||
1258 | 1276 | /* |
1259 | 1277 | /********************************************************************** |
1260 | 1278 | /* Second-level decoding, Number decoding |
1476 | 1494 | } else { // Number between 0 and 9 |
1477 | 1495 | // although not guaranteed, seems likely valid separator (white space, |
1478 | 1496 | // comma, end bracket/curly); next time token needed will verify |
1479 | if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { | |
1497 | if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { | |
1480 | 1498 | reportInvalidNumber("Leading zeroes not allowed"); |
1481 | 1499 | } |
1482 | 1500 | if (ch == INT_0) { // coalesce multiple leading zeroes into just one |
1529 | 1547 | } else { // Number between 1 and 9; go integral |
1530 | 1548 | // although not guaranteed, seems likely valid separator (white space, |
1531 | 1549 | // comma, end bracket/curly); next time token needed will verify |
1532 | if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { | |
1550 | if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { | |
1533 | 1551 | reportInvalidNumber("Leading zeroes not allowed"); |
1534 | 1552 | } |
1535 | 1553 | if (ch == INT_0) { // coalesce multiple leading zeroes into just one |
2048 | 2066 | case '#': |
2049 | 2067 | // Careful, since this may alternatively be leading char of |
2050 | 2068 | // unquoted name... |
2051 | if (JsonParser.Feature.ALLOW_YAML_COMMENTS.enabledIn(_features)) { | |
2069 | if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) != 0) { | |
2052 | 2070 | return _finishHashComment(MINOR_FIELD_LEADING_WS); |
2053 | 2071 | } |
2054 | 2072 | break; |
2055 | 2073 | case '/': |
2056 | 2074 | return _startSlashComment(MINOR_FIELD_LEADING_WS); |
2057 | 2075 | case '\'': |
2058 | if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { | |
2076 | if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { | |
2059 | 2077 | return _finishAposName(0, 0, 0); |
2060 | 2078 | } |
2061 | 2079 | break; |
2062 | case ']': // for better error reporting... | |
2080 | case INT_RBRACKET: // for better error reporting... | |
2063 | 2081 | return _closeArrayScope(); |
2064 | 2082 | } |
2065 | 2083 | // allow unquoted names if feature enabled: |
2066 | if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { | |
2084 | if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { | |
2067 | 2085 | // !!! TODO: Decode UTF-8 characters properly... |
2068 | 2086 | // char c = (char) _decodeCharForError(ch); |
2069 | 2087 | char c = (char) ch; |
835 | 835 | return t; |
836 | 836 | } |
837 | 837 | |
838 | @SuppressWarnings("deprecation") | |
838 | 839 | protected final JsonToken _valueNonStdNumberComplete(int type) throws IOException |
839 | 840 | { |
840 | 841 | String tokenStr = NON_STD_TOKENS[type]; |
10 | 10 | * memory access due to flattening of name quad data. |
11 | 11 | * Performance improvement modest for simple JSON document data binding (maybe 3%), |
12 | 12 | * but should help more for larger symbol tables, or for binary formats like Smile. |
13 | *<p> | |
14 | * Hash area is divided into 4 sections: | |
15 | *<ol> | |
16 | * <li>Primary area (1/2 of total size), direct match from hash (LSB)</li> | |
17 | * <li>Secondary area (1/4 of total size), match from {@code hash (LSB) >> 1}</li> | |
18 | * <li>Tertiary area (1/8 of total size), match from {@code hash (LSB) >> 2}</li> | |
19 | * <li>Spill-over area (remaining 1/8) with linear scan, insertion order</li> | |
20 | * <li></li> | |
21 | * </ol> | |
22 | * and within every area, entries are 4 {@code int}s, where 1 - 3 ints contain 1 - 12 | |
23 | * UTF-8 encoded bytes of name (null-padded), and last int is offset in | |
24 | * {@code _names} that contains actual name Strings. | |
13 | 25 | * |
14 | 26 | * @since 2.6 |
15 | 27 | */ |
17 | 29 | { |
18 | 30 | /** |
19 | 31 | * Initial size of the primary hash area. Each entry consumes 4 ints (16 bytes), |
20 | * and secondary area is same as primary; so default size will use 2kB of memory_tertiaryStart | |
32 | * and secondary area is same as primary; so default size will use 2kB of memory | |
21 | 33 | * (plus 64x4 or 64x8 (256/512 bytes) for references to Strings, and Strings |
22 | 34 | * themselves). |
23 | 35 | */ |
26 | 38 | |
27 | 39 | /** |
28 | 40 | * Let's not expand symbol tables past some maximum size; |
29 | * this should protected against OOMEs caused by large documents | |
30 | 41 | * with unique (~= random) names. |
31 | 42 | * Size is in |
32 | 43 | */ |
168 | 179 | |
169 | 180 | /** |
170 | 181 | * Offset within {@link #_hashArea} that follows main slots and contains |
171 | * quads for longer names (13 bytes or longers), and points to the | |
182 | * quads for longer names (13 bytes or longer), and points to the | |
172 | 183 | * first available int that may be used for appending quads of the next |
173 | 184 | * long name. |
174 | 185 | * Note that long name area follows immediately after the fixed-size |
175 | 186 | * main hash area ({@link #_hashArea}). |
176 | 187 | */ |
177 | 188 | private int _longNameOffset; |
178 | ||
179 | /** | |
180 | * This flag is set if, after adding a new entry, it is deemed | |
181 | * that a rehash is warranted if any more entries are to be added. | |
182 | */ | |
183 | private transient boolean _needRehash; | |
184 | 189 | |
185 | 190 | /* |
186 | 191 | /********************************************************** |
266 | 271 | _longNameOffset = state.longNameOffset; |
267 | 272 | |
268 | 273 | // and then set other state to reflect sharing status |
269 | _needRehash = false; | |
270 | 274 | _hashShared = true; |
271 | 275 | } |
272 | 276 | |
316 | 320 | public void release() |
317 | 321 | { |
318 | 322 | // we will try to merge if child table has new entries |
319 | if (_parent != null && maybeDirty()) { | |
323 | // 28-Jul-2019, tatu: From [core#548]: do not share if immediate rehash needed | |
324 | if ((_parent != null) && maybeDirty()) { | |
320 | 325 | _parent.mergeChild(new TableInfo(this)); |
321 | 326 | // Let's also mark this instance as dirty, so that just in |
322 | 327 | // case release was too early, there's no corruption of possibly shared data. |
767 | 772 | _hashArea[offset+3] = 1; |
768 | 773 | _names[offset >> 2] = name; |
769 | 774 | ++_count; |
770 | _verifyNeedForRehash(); | |
771 | 775 | return name; |
772 | 776 | } |
773 | 777 | |
783 | 787 | _hashArea[offset+3] = 2; |
784 | 788 | _names[offset >> 2] = name; |
785 | 789 | ++_count; |
786 | _verifyNeedForRehash(); | |
787 | 790 | return name; |
788 | 791 | } |
789 | 792 | |
799 | 802 | _hashArea[offset+3] = 3; |
800 | 803 | _names[offset >> 2] = name; |
801 | 804 | ++_count; |
802 | _verifyNeedForRehash(); | |
803 | 805 | return name; |
804 | 806 | } |
805 | 807 | |
850 | 852 | |
851 | 853 | // and finally; see if we really should rehash. |
852 | 854 | ++_count; |
853 | _verifyNeedForRehash(); | |
854 | 855 | return name; |
855 | } | |
856 | ||
857 | private void _verifyNeedForRehash() { | |
858 | // Yes if above 80%, or above 50% AND have ~1% spill-overs | |
859 | if (_count > (_hashSize >> 1)) { // over 50% | |
860 | int spillCount = (_spilloverEnd - _spilloverStart()) >> 2; | |
861 | if ((spillCount > (1 + _count >> 7)) | |
862 | || (_count > (_hashSize * 0.80))) { | |
863 | _needRehash = true; | |
864 | } | |
865 | } | |
866 | 856 | } |
867 | 857 | |
868 | 858 | private void _verifySharing() |
871 | 861 | _hashArea = Arrays.copyOf(_hashArea, _hashArea.length); |
872 | 862 | _names = Arrays.copyOf(_names, _names.length); |
873 | 863 | _hashShared = false; |
874 | // 09-Sep-2015, tatu: As per [jackson-core#216], also need to ensure | |
875 | // we rehash as needed, as need-rehash flag is not copied from parent | |
876 | _verifyNeedForRehash(); | |
877 | } | |
878 | if (_needRehash) { | |
879 | rehash(); | |
880 | 864 | } |
881 | 865 | } |
882 | 866 | |
885 | 869 | */ |
886 | 870 | private int _findOffsetForAdd(int hash) |
887 | 871 | { |
888 | // first, check the primary: | |
872 | // first, check the primary: if slot found, no need for resize | |
889 | 873 | int offset = _calcOffset(hash); |
890 | 874 | final int[] hashArea = _hashArea; |
891 | 875 | if (hashArea[offset+3] == 0) { |
892 | 876 | //System.err.printf(" PRImary slot #%d, hash %X\n", (offset>>2), hash & 0x7F); |
893 | 877 | return offset; |
894 | 878 | } |
895 | // then secondary | |
879 | ||
880 | // Otherwise let's see if we are due resize(): | |
881 | if (_checkNeedForRehash()) { | |
882 | return _resizeAndFindOffsetForAdd(hash); | |
883 | } | |
884 | ||
885 | // If not, proceed with secondary slot | |
896 | 886 | int offset2 = _secondaryStart + ((offset >> 3) << 2); |
897 | 887 | if (hashArea[offset2+3] == 0) { |
898 | 888 | //System.err.printf(" SECondary slot #%d (start x%X), hash %X\n",(offset >> 3), _secondaryStart, (hash & 0x7F)); |
926 | 916 | if (_failOnDoS) { |
927 | 917 | _reportTooManyCollisions(); |
928 | 918 | } |
929 | // and if we didn't fail, we'll simply force rehash for next add | |
930 | // (which, in turn, may double up or nuke contents, depending on size etc) | |
931 | _needRehash = true; | |
919 | return _resizeAndFindOffsetForAdd(hash); | |
932 | 920 | } |
933 | 921 | return offset; |
934 | 922 | } |
935 | 923 | |
924 | // @since 2.10 | |
925 | private int _resizeAndFindOffsetForAdd(int hash) | |
926 | { | |
927 | // First things first: we need to resize+rehash (or, if too big, nuke contents) | |
928 | rehash(); | |
929 | ||
930 | // Copy of main _findOffsetForAdd except for checks to resize: can not be needed | |
931 | int offset = _calcOffset(hash); | |
932 | final int[] hashArea = _hashArea; | |
933 | if (hashArea[offset+3] == 0) { | |
934 | return offset; | |
935 | } | |
936 | int offset2 = _secondaryStart + ((offset >> 3) << 2); | |
937 | if (hashArea[offset2+3] == 0) { | |
938 | return offset2; | |
939 | } | |
940 | offset2 = _tertiaryStart + ((offset >> (_tertiaryShift + 2)) << _tertiaryShift); | |
941 | final int bucketSize = (1 << _tertiaryShift); | |
942 | for (int end = offset2 + bucketSize; offset2 < end; offset2 += 4) { | |
943 | if (hashArea[offset2+3] == 0) { | |
944 | return offset2; | |
945 | } | |
946 | } | |
947 | offset = _spilloverEnd; | |
948 | _spilloverEnd += 4; | |
949 | return offset; | |
950 | } | |
951 | ||
952 | // Helper method for checking if we should simply rehash() before add | |
953 | private boolean _checkNeedForRehash() { | |
954 | // Yes if above 80%, or above 50% AND have ~1% spill-overs | |
955 | if (_count > (_hashSize >> 1)) { // over 50% | |
956 | int spillCount = (_spilloverEnd - _spilloverStart()) >> 2; | |
957 | if ((spillCount > (1 + _count >> 7)) | |
958 | || (_count > (_hashSize * 0.80))) { | |
959 | return true; | |
960 | } | |
961 | } | |
962 | return false; | |
963 | } | |
964 | ||
936 | 965 | private int _appendLongName(int[] quads, int qlen) |
937 | 966 | { |
938 | 967 | int start = _longNameOffset; |
1060 | 1089 | |
1061 | 1090 | private void rehash() |
1062 | 1091 | { |
1063 | _needRehash = false; | |
1064 | 1092 | // Note: since we'll make copies, no need to unshare, can just mark as such: |
1065 | 1093 | _hashShared = false; |
1066 | 1094 |
77 | 77 | /** |
78 | 78 | * Also: to thwart attacks based on hash collisions (which may or may not |
79 | 79 | * be cheap to calculate), we will need to detect "too long" |
80 | * collision chains. Let's start with static value of 255 entries | |
80 | * collision chains. Let's start with static value of 100 entries | |
81 | 81 | * for the longest legal chain. |
82 | 82 | *<p> |
83 | 83 | * Note: longest chain we have been able to produce without malicious |
84 | 84 | * intent has been 38 (with "com.fasterxml.jackson.core.main.TestWithTonsaSymbols"); |
85 | 85 | * our setting should be reasonable here. |
86 | *<p> | |
87 | * Also note that value was lowered from 255 (2.3 and earlier) to 100 for 2.4 | |
88 | 86 | * |
89 | 87 | * @since 2.1 |
90 | 88 | */ |
480 | 478 | _hashShared = false; |
481 | 479 | } else if (_size >= _sizeThreshold) { // Need to expand? |
482 | 480 | rehash(); |
483 | /* Need to recalc hash; rare occurence (index mask has been | |
484 | * recalculated as part of rehash) | |
485 | */ | |
481 | // Need to recalc hash; rare occurrence (index mask has been | |
482 | // recalculated as part of rehash) | |
486 | 483 | index = _hashToIndex(calcHash(buffer, start, len)); |
487 | 484 | } |
488 | 485 | |
501 | 498 | if (collLen > MAX_COLL_CHAIN_LENGTH) { |
502 | 499 | // 23-May-2014, tatu: Instead of throwing an exception right away, |
503 | 500 | // let's handle in bit smarter way. |
504 | _handleSpillOverflow(bix, newB); | |
501 | _handleSpillOverflow(bix, newB, index); | |
505 | 502 | } else { |
506 | 503 | _buckets[bix] = newB; |
507 | 504 | _longestCollisionList = Math.max(collLen, _longestCollisionList); |
510 | 507 | return newSymbol; |
511 | 508 | } |
512 | 509 | |
513 | private void _handleSpillOverflow(int bindex, Bucket newBucket) | |
510 | /** | |
511 | * Method called when an overflow bucket has hit the maximum expected length: | |
512 | * this may be a case of DoS attack. Deal with it based on settings by either | |
513 | * clearing up bucket (to avoid indefinite expansion) or throwing exception. | |
514 | * Currently the first overflow for any single bucket DOES NOT throw an exception, | |
515 | * only second time (per symbol table instance) | |
516 | */ | |
517 | private void _handleSpillOverflow(int bucketIndex, Bucket newBucket, int mainIndex) | |
514 | 518 | { |
515 | 519 | if (_overflows == null) { |
516 | 520 | _overflows = new BitSet(); |
517 | _overflows.set(bindex); | |
521 | _overflows.set(bucketIndex); | |
518 | 522 | } else { |
519 | if (_overflows.get(bindex)) { | |
520 | // Has happened once already, so not a coincident... | |
523 | if (_overflows.get(bucketIndex)) { | |
524 | // Has happened once already for this bucket index, so probably not coincidental... | |
521 | 525 | if (JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW.enabledIn(_flags)) { |
522 | 526 | reportTooManyCollisions(MAX_COLL_CHAIN_LENGTH); |
523 | 527 | } |
524 | // but even if we don't fail, we will stop canonicalizing: | |
528 | // but even if we don't fail, we will stop canonicalizing as safety measure | |
529 | // (so as not to cause problems with PermGen) | |
525 | 530 | _canonicalize = false; |
526 | 531 | } else { |
527 | _overflows.set(bindex); | |
528 | } | |
529 | } | |
532 | _overflows.set(bucketIndex); | |
533 | } | |
534 | } | |
535 | ||
530 | 536 | // regardless, if we get this far, clear up the bucket, adjust size appropriately. |
531 | _symbols[bindex + bindex] = newBucket.symbol; | |
532 | _buckets[bindex] = null; | |
533 | // newBucket contains new symbol; but we wil | |
537 | _symbols[mainIndex] = newBucket.symbol; | |
538 | _buckets[bucketIndex] = null; | |
539 | // newBucket contains new symbol; but we will | |
534 | 540 | _size -= (newBucket.length); |
535 | 541 | // we could calculate longest; but for now just mark as invalid |
536 | 542 | _longestCollisionList = -1; |
603 | 609 | * entries. |
604 | 610 | */ |
605 | 611 | private void rehash() { |
606 | int size = _symbols.length; | |
612 | final int size = _symbols.length; | |
607 | 613 | int newSize = size + size; |
608 | 614 | |
609 | 615 | /* 12-Mar-2010, tatu: Let's actually limit maximum size we are |
623 | 629 | return; |
624 | 630 | } |
625 | 631 | |
626 | String[] oldSyms = _symbols; | |
627 | Bucket[] oldBuckets = _buckets; | |
632 | final String[] oldSyms = _symbols; | |
633 | final Bucket[] oldBuckets = _buckets; | |
628 | 634 | _symbols = new String[newSize]; |
629 | 635 | _buckets = new Bucket[newSize >> 1]; |
630 | 636 | // Let's update index mask, threshold, now (needed for rehashing) |
652 | 658 | } |
653 | 659 | } |
654 | 660 | |
655 | size >>= 1; | |
656 | for (int i = 0; i < size; ++i) { | |
661 | final int bucketSize = (size >> 1); | |
662 | for (int i = 0; i < bucketSize; ++i) { | |
657 | 663 | Bucket b = oldBuckets[i]; |
658 | 664 | while (b != null) { |
659 | 665 | ++count; |
688 | 694 | +") now exceeds maximum, "+maxLen+" -- suspect a DoS attack based on hash collisions"); |
689 | 695 | } |
690 | 696 | |
697 | // since 2.10, for tests only | |
698 | /** | |
699 | * Diagnostics method that will verify that internal data structures are consistent; | |
700 | * not meant as user-facing method but only for test suites and possible troubleshooting. | |
701 | * | |
702 | * @since 2.10 | |
703 | */ | |
704 | protected void verifyInternalConsistency() { | |
705 | int count = 0; | |
706 | final int size = _symbols.length; | |
707 | ||
708 | for (int i = 0; i < size; ++i) { | |
709 | String symbol = _symbols[i]; | |
710 | if (symbol != null) { | |
711 | ++count; | |
712 | } | |
713 | } | |
714 | ||
715 | final int bucketSize = (size >> 1); | |
716 | for (int i = 0; i < bucketSize; ++i) { | |
717 | for (Bucket b = _buckets[i]; b != null; b = b.next) { | |
718 | ++count; | |
719 | } | |
720 | } | |
721 | if (count != _size) { | |
722 | throw new IllegalStateException(String.format("Internal error: expected internal size %d vs calculated count %d", | |
723 | _size, count)); | |
724 | } | |
725 | } | |
726 | ||
691 | 727 | // For debugging, comment out |
692 | 728 | /* |
693 | 729 | @Override |
0 | 0 | package com.fasterxml.jackson.core.util; |
1 | ||
2 | import java.util.concurrent.atomic.AtomicReferenceArray; | |
1 | 3 | |
2 | 4 | /** |
3 | 5 | * This is a small utility class, whose main functionality is to allow |
6 | 8 | * instance of this class through a <code>SoftReference</code>. The |
7 | 9 | * end result is a low-overhead GC-cleanable recycling: hopefully |
8 | 10 | * ideal for use by stream readers. |
11 | *<p> | |
12 | * Rewritten in 2.10 to be thread-safe (see [jackson-core#479] for details), | |
13 | * to not rely on {@code ThreadLocal} access. | |
9 | 14 | */ |
10 | 15 | public class BufferRecycler |
11 | 16 | { |
34 | 39 | */ |
35 | 40 | public final static int BYTE_BASE64_CODEC_BUFFER = 3; |
36 | 41 | |
37 | public final static int CHAR_TOKEN_BUFFER = 0; // Tokenizable input | |
38 | public final static int CHAR_CONCAT_BUFFER = 1; // concatenated output | |
39 | public final static int CHAR_TEXT_BUFFER = 2; // Text content from input | |
40 | public final static int CHAR_NAME_COPY_BUFFER = 3; // Temporary buffer for getting name characters | |
42 | /** | |
43 | * Buffer used as input buffer for tokenization for character-based parsers. | |
44 | */ | |
45 | public final static int CHAR_TOKEN_BUFFER = 0; | |
41 | 46 | |
42 | // Buffer lengths, defined in 2.4 (smaller before that) | |
47 | /** | |
48 | * Buffer used by generators; for byte-backed generators for buffering of | |
49 | * {@link String} values to output (before encoding into UTF-8), | |
50 | * and for char-backed generators as actual concatenation buffer. | |
51 | */ | |
52 | public final static int CHAR_CONCAT_BUFFER = 1; | |
53 | ||
54 | /** | |
55 | * Used through {@link TextBuffer}: directly by parsers (to concatenate | |
56 | * String values) | |
57 | * and indirectly via | |
58 | * {@link com.fasterxml.jackson.core.io.SegmentedStringWriter} | |
59 | * when serializing (databind level {@code ObjectMapper} and | |
60 | * {@code ObjectWriter}). In both cases used as segments (and not for whole value), | |
61 | * but may result in retention of larger chunks for big content | |
62 | * (long text values during parsing; bigger output documents for generation). | |
63 | */ | |
64 | public final static int CHAR_TEXT_BUFFER = 2; | |
65 | ||
66 | /** | |
67 | * For parsers, temporary buffer into which {@code char[]} for names is copied | |
68 | * when requested as such; for {@code WriterBasedGenerator} used for buffering | |
69 | * during {@code writeString(Reader)} operation (not commonly used). | |
70 | */ | |
71 | public final static int CHAR_NAME_COPY_BUFFER = 3; | |
72 | ||
73 | // Buffer lengths | |
43 | 74 | |
44 | 75 | private final static int[] BYTE_BUFFER_LENGTHS = new int[] { 8000, 8000, 2000, 2000 }; |
45 | 76 | private final static int[] CHAR_BUFFER_LENGTHS = new int[] { 4000, 4000, 200, 200 }; |
46 | ||
47 | final protected byte[][] _byteBuffers; | |
48 | final protected char[][] _charBuffers; | |
77 | ||
78 | // Note: changed from simple array in 2.10: | |
79 | protected final AtomicReferenceArray<byte[]> _byteBuffers; | |
80 | ||
81 | // Note: changed from simple array in 2.10: | |
82 | protected final AtomicReferenceArray<char[]> _charBuffers; | |
49 | 83 | |
50 | 84 | /* |
51 | 85 | /********************************************************** |
52 | 86 | /* Construction |
53 | 87 | /********************************************************** |
54 | 88 | */ |
55 | ||
89 | ||
56 | 90 | /** |
57 | 91 | * Default constructor used for creating instances of this default |
58 | 92 | * implementation. |
68 | 102 | * @since 2.4 |
69 | 103 | */ |
70 | 104 | protected BufferRecycler(int bbCount, int cbCount) { |
71 | _byteBuffers = new byte[bbCount][]; | |
72 | _charBuffers = new char[cbCount][]; | |
105 | _byteBuffers = new AtomicReferenceArray<byte[]>(bbCount); | |
106 | _charBuffers = new AtomicReferenceArray<char[]>(cbCount); | |
73 | 107 | } |
74 | 108 | |
75 | 109 | /* |
90 | 124 | if (minSize < DEF_SIZE) { |
91 | 125 | minSize = DEF_SIZE; |
92 | 126 | } |
93 | byte[] buffer = _byteBuffers[ix]; | |
127 | byte[] buffer = _byteBuffers.getAndSet(ix, null); | |
94 | 128 | if (buffer == null || buffer.length < minSize) { |
95 | 129 | buffer = balloc(minSize); |
96 | } else { | |
97 | _byteBuffers[ix] = null; | |
98 | 130 | } |
99 | 131 | return buffer; |
100 | 132 | } |
101 | 133 | |
102 | 134 | public void releaseByteBuffer(int ix, byte[] buffer) { |
103 | _byteBuffers[ix] = buffer; | |
135 | _byteBuffers.set(ix, buffer); | |
104 | 136 | } |
105 | 137 | |
106 | 138 | /* |
118 | 150 | if (minSize < DEF_SIZE) { |
119 | 151 | minSize = DEF_SIZE; |
120 | 152 | } |
121 | char[] buffer = _charBuffers[ix]; | |
153 | char[] buffer = _charBuffers.getAndSet(ix, null); | |
122 | 154 | if (buffer == null || buffer.length < minSize) { |
123 | 155 | buffer = calloc(minSize); |
124 | } else { | |
125 | _charBuffers[ix] = null; | |
126 | 156 | } |
127 | 157 | return buffer; |
128 | 158 | } |
129 | 159 | |
130 | 160 | public void releaseCharBuffer(int ix, char[] buffer) { |
131 | _charBuffers[ix] = buffer; | |
161 | _charBuffers.set(ix, buffer); | |
132 | 162 | } |
133 | 163 | |
134 | 164 | /* |
144 | 174 | protected int charBufferLength(int ix) { |
145 | 175 | return CHAR_BUFFER_LENGTHS[ix]; |
146 | 176 | } |
147 | ||
177 | ||
148 | 178 | /* |
149 | 179 | /********************************************************** |
150 | 180 | /* Actual allocations separated for easier debugging/profiling |
0 | 0 | package com.fasterxml.jackson.core.util; |
1 | 1 | |
2 | 2 | import java.lang.ref.SoftReference; |
3 | ||
4 | import com.fasterxml.jackson.core.io.JsonStringEncoder; | |
5 | 3 | |
6 | 4 | /** |
7 | 5 | * Helper entity used to control access to simple buffer recyling scheme used for |
8 | 6 | * some encoding, decoding tasks. |
9 | 7 | * |
10 | 8 | * @see BufferRecycler |
11 | * @see JsonStringEncoder | |
12 | 9 | * |
13 | 10 | * @since 2.9.2 |
14 | 11 | */ |
93 | 90 | } |
94 | 91 | return -1; |
95 | 92 | } |
96 | ||
97 | /* | |
98 | /********************************************************** | |
99 | /* JsonStringEncoder | |
100 | /********************************************************** | |
101 | */ | |
102 | ||
103 | /** | |
104 | * This <code>ThreadLocal</code> contains a {@link java.lang.ref.SoftReference} | |
105 | * to a {@link BufferRecycler} used to provide a low-cost | |
106 | * buffer recycling between reader and writer instances. | |
107 | */ | |
108 | final protected static ThreadLocal<SoftReference<JsonStringEncoder>> _encoderRef | |
109 | = new ThreadLocal<SoftReference<JsonStringEncoder>>(); | |
110 | ||
111 | public static JsonStringEncoder getJsonStringEncoder() { | |
112 | SoftReference<JsonStringEncoder> ref = _encoderRef.get(); | |
113 | JsonStringEncoder enc = (ref == null) ? null : ref.get(); | |
114 | ||
115 | if (enc == null) { | |
116 | enc = new JsonStringEncoder(); | |
117 | _encoderRef.set(new SoftReference<JsonStringEncoder>(enc)); | |
118 | } | |
119 | return enc; | |
120 | } | |
121 | ||
122 | /** | |
123 | * Helper method for encoding given String as UTF-8 encoded | |
124 | * | |
125 | * @since 2.9.4 | |
126 | */ | |
127 | public static byte[] encodeAsUTF8(String text) { | |
128 | return getJsonStringEncoder().encodeAsUTF8(text); | |
129 | } | |
130 | ||
131 | /** | |
132 | * @since 2.9.4 | |
133 | */ | |
134 | public static char[] quoteAsJsonText(String rawText) { | |
135 | return getJsonStringEncoder().quoteAsString(rawText); | |
136 | } | |
137 | ||
138 | /** | |
139 | * @since 2.9.4 | |
140 | */ | |
141 | public static void quoteAsJsonText(CharSequence input, StringBuilder output) { | |
142 | getJsonStringEncoder().quoteAsString(input, output); | |
143 | } | |
144 | ||
145 | /** | |
146 | * @since 2.9.4 | |
147 | */ | |
148 | public static byte[] quoteAsJsonUTF8(String rawText) { | |
149 | return getJsonStringEncoder().quoteAsUTF8(rawText); | |
150 | } | |
151 | 93 | } |
32 | 32 | // Size of the first block we will allocate. |
33 | 33 | private final static int INITIAL_BLOCK_SIZE = 500; |
34 | 34 | |
35 | // Maximum block size we will use for individual non-aggregated | |
36 | // blocks. Let's limit to using 256k chunks. | |
37 | private final static int MAX_BLOCK_SIZE = (1 << 18); | |
38 | ||
35 | // Maximum block size we will use for individual non-aggregated blocks. | |
36 | // For 2.10, let's limit to using 128k chunks (was 256k up to 2.9) | |
37 | private final static int MAX_BLOCK_SIZE = (1 << 17); | |
38 | ||
39 | 39 | final static int DEFAULT_BLOCK_ARRAY_SIZE = 40; |
40 | 40 | |
41 | 41 | // Optional buffer recycler instance that we can use for allocating the first block. |
56 | 56 | _currBlock = (br == null) ? new byte[firstBlockSize] : br.allocByteBuffer(BufferRecycler.BYTE_WRITE_CONCAT_BUFFER); |
57 | 57 | } |
58 | 58 | |
59 | private ByteArrayBuilder(BufferRecycler br, byte[] initialBlock, int initialLen) { | |
60 | _bufferRecycler = null; | |
61 | _currBlock = initialBlock; | |
62 | _currBlockPtr = initialLen; | |
63 | } | |
64 | ||
65 | public static ByteArrayBuilder fromInitial(byte[] initialBlock, int length) { | |
66 | return new ByteArrayBuilder(null, initialBlock, length); | |
67 | } | |
68 | ||
59 | 69 | public void reset() { |
60 | 70 | _pastLen = 0; |
61 | 71 | _currBlockPtr = 0; |
156 | 156 | } |
157 | 157 | |
158 | 158 | /** |
159 | * @since 2.6.0 | |
159 | * @since 2.6 | |
160 | 160 | */ |
161 | 161 | public DefaultPrettyPrinter withRootSeparator(String rootSeparator) { |
162 | 162 | return withRootSeparator((rootSeparator == null) ? null : new SerializedString(rootSeparator)); |
251 | 251 | |
252 | 252 | @Override |
253 | 253 | public DefaultPrettyPrinter createInstance() { |
254 | if (getClass() != DefaultPrettyPrinter.class) { // since 2.10 | |
255 | throw new IllegalStateException("Failed `createInstance()`: "+getClass().getName() | |
256 | +" does not override method; it has to"); | |
257 | } | |
254 | 258 | return new DefaultPrettyPrinter(this); |
255 | 259 | } |
256 | 260 |
194 | 194 | |
195 | 195 | @Override |
196 | 196 | public void writeStartArray(int size) throws IOException { delegate.writeStartArray(size); } |
197 | ||
197 | ||
198 | @Override | |
199 | public void writeStartArray(Object forValue) throws IOException { delegate.writeStartArray(forValue); } | |
200 | ||
201 | @Override | |
202 | public void writeStartArray(Object forValue, int size) throws IOException { delegate.writeStartArray(forValue, size); } | |
203 | ||
198 | 204 | @Override |
199 | 205 | public void writeEndArray() throws IOException { delegate.writeEndArray(); } |
200 | 206 | |
203 | 209 | |
204 | 210 | @Override |
205 | 211 | public void writeStartObject(Object forValue) throws IOException { delegate.writeStartObject(forValue); } |
212 | ||
213 | @Override | |
214 | public void writeStartObject(Object forValue, int size) throws IOException { | |
215 | delegate.writeStartObject(forValue, size); | |
216 | } | |
206 | 217 | |
207 | 218 | @Override |
208 | 219 | public void writeEndObject() throws IOException { delegate.writeEndObject(); } |
236 | 236 | @Override public boolean canReadTypeId() { return delegate.canReadTypeId(); } |
237 | 237 | @Override public Object getObjectId() throws IOException { return delegate.getObjectId(); } |
238 | 238 | @Override public Object getTypeId() throws IOException { return delegate.getTypeId(); } |
239 | ||
240 | /* | |
241 | /********************************************************** | |
242 | /* Extended API | |
243 | /********************************************************** | |
244 | */ | |
245 | ||
246 | /** | |
247 | * Accessor for getting the immediate {@link JsonParser} this parser delegates calls to. | |
248 | * | |
249 | * @since 2.10 | |
250 | */ | |
251 | public JsonParser delegate() { return delegate; } | |
239 | 252 | } |
29 | 29 | |
30 | 30 | /** |
31 | 31 | * Let's start with sizable but not huge buffer, will grow as necessary |
32 | */ | |
33 | final static int MIN_SEGMENT_LEN = 1000; | |
34 | ||
35 | /** | |
36 | * Let's limit maximum segment length to something sensible | |
37 | * like 256k | |
38 | */ | |
39 | final static int MAX_SEGMENT_LEN = 0x40000; | |
32 | *<p> | |
33 | * Reduced from 1000 down to 500 in 2.10. | |
34 | */ | |
35 | final static int MIN_SEGMENT_LEN = 500; | |
36 | ||
37 | /** | |
38 | * Let's limit maximum segment length to something sensible. | |
39 | * For 2.10, let's limit to using 64kc chunks (128 kB) -- was 256kC/512kB up to 2.9 | |
40 | */ | |
41 | final static int MAX_SEGMENT_LEN = 0x10000; | |
40 | 42 | |
41 | 43 | /* |
42 | 44 | /********************************************************** |
119 | 121 | |
120 | 122 | public TextBuffer(BufferRecycler allocator) { |
121 | 123 | _allocator = allocator; |
124 | } | |
125 | ||
126 | /** | |
127 | * @since 2.10 | |
128 | */ | |
129 | protected TextBuffer(BufferRecycler allocator, char[] initialSegment) { | |
130 | _allocator = allocator; | |
131 | _currentSegment = initialSegment; | |
132 | _currentSize = initialSegment.length; | |
133 | _inputStart = -1; | |
134 | } | |
135 | ||
136 | /** | |
137 | * Factory method for constructing an instance with no allocator, and | |
138 | * with initial full segment. | |
139 | * | |
140 | * @since 2.10 | |
141 | */ | |
142 | public static TextBuffer fromInitial(char[] initialSegment) { | |
143 | return new TextBuffer(null, initialSegment); | |
122 | 144 | } |
123 | 145 | |
124 | 146 | /** |
0 | // Generated 08-Mar-2019 using Moditect maven plugin | |
1 | module com.fasterxml.jackson.core { | |
2 | // 08-Mar-2019, tatu: Ugh. Can not use wildcards, stupid ass JDK 9+ module system... | |
3 | // So, for 2.x core need to make sure we manually include everything. | |
4 | // Worse, there is only syntactic validation, not contents, so we can both miss | |
5 | // AND add bogus packages. | |
6 | // However: at least syntax is verified; and this works with JKD8 | |
7 | exports com.fasterxml.jackson.core; | |
8 | exports com.fasterxml.jackson.core.async; | |
9 | exports com.fasterxml.jackson.core.base; | |
10 | exports com.fasterxml.jackson.core.exc; | |
11 | exports com.fasterxml.jackson.core.filter; | |
12 | exports com.fasterxml.jackson.core.format; | |
13 | exports com.fasterxml.jackson.core.io; | |
14 | exports com.fasterxml.jackson.core.json; | |
15 | exports com.fasterxml.jackson.core.json.async; | |
16 | exports com.fasterxml.jackson.core.sym; | |
17 | exports com.fasterxml.jackson.core.type; | |
18 | exports com.fasterxml.jackson.core.util; | |
19 | } |
301 | 301 | |
302 | 302 | /* |
303 | 303 | /********************************************************** |
304 | /* Parser/generator construction | |
304 | /* Parser construction | |
305 | 305 | /********************************************************** |
306 | 306 | */ |
307 | 307 | |
397 | 397 | |
398 | 398 | /* |
399 | 399 | /********************************************************** |
400 | /* Generator construction | |
401 | /********************************************************** | |
402 | */ | |
403 | ||
404 | protected JsonGenerator createGenerator(OutputStream out) throws IOException { | |
405 | return createGenerator(JSON_FACTORY, out); | |
406 | } | |
407 | ||
408 | protected JsonGenerator createGenerator(TokenStreamFactory f, OutputStream out) throws IOException { | |
409 | return f.createGenerator(out); | |
410 | } | |
411 | ||
412 | protected JsonGenerator createGenerator(Writer w) throws IOException { | |
413 | return createGenerator(JSON_FACTORY, w); | |
414 | } | |
415 | ||
416 | protected JsonGenerator createGenerator(TokenStreamFactory f, Writer w) throws IOException { | |
417 | return f.createGenerator(w); | |
418 | } | |
419 | ||
420 | /* | |
421 | /********************************************************** | |
400 | 422 | /* Helper read/write methods |
401 | 423 | /********************************************************** |
402 | 424 | */ |
403 | ||
425 | ||
404 | 426 | protected void writeJsonDoc(JsonFactory f, String doc, Writer w) throws IOException |
405 | 427 | { |
406 | 428 | writeJsonDoc(f, doc, f.createGenerator(w)); |
565 | 587 | protected JsonFactory newStreamFactory() { |
566 | 588 | return new JsonFactory(); |
567 | 589 | } |
568 | ||
590 | ||
591 | // @since 2.9.8 | |
592 | protected JsonFactoryBuilder streamFactoryBuilder() { | |
593 | return (JsonFactoryBuilder) JsonFactory.builder(); | |
594 | } | |
595 | ||
569 | 596 | protected String fieldNameFor(int index) |
570 | 597 | { |
571 | 598 | StringBuilder sb = new StringBuilder(16); |
81 | 81 | |
82 | 82 | public void testEmpty() |
83 | 83 | { |
84 | assertSame(JsonPointer.EMPTY, JsonPointer.empty()); | |
85 | assertSame(JsonPointer.EMPTY, JsonPointer.compile("")); | |
86 | } | |
87 | ||
88 | public void testEmptyName() | |
89 | { | |
84 | 90 | // note: this is acceptable, to match property in '{"":3}', for example |
85 | 91 | // and NOT same as what empty point, "", is. |
86 | 92 | JsonPointer ptr = JsonPointer.compile("/"); |
87 | 93 | assertNotNull(ptr); |
88 | 94 | assertNotSame(JsonPointer.EMPTY, ptr); |
95 | ||
89 | 96 | assertEquals("/", ptr.toString()); |
97 | } | |
98 | ||
99 | // mostly for test coverage, really... | |
100 | public void testEquality() { | |
101 | assertFalse(JsonPointer.empty().equals(JsonPointer.compile("/"))); | |
102 | ||
103 | assertEquals(JsonPointer.compile("/foo/3"), JsonPointer.compile("/foo/3")); | |
104 | assertFalse(JsonPointer.empty().equals(JsonPointer.compile("/12"))); | |
105 | assertFalse(JsonPointer.compile("/12").equals(JsonPointer.empty())); | |
106 | ||
107 | // expr != String | |
108 | assertFalse(JsonPointer.empty().equals("/")); | |
109 | } | |
110 | ||
111 | public void testProperties() { | |
112 | assertTrue(JsonPointer.compile("/foo").mayMatchProperty()); | |
113 | assertFalse(JsonPointer.compile("/foo").mayMatchElement()); | |
114 | ||
115 | assertTrue(JsonPointer.compile("/12").mayMatchElement()); | |
116 | // Interestingly enough, since Json Pointer is just String, could | |
117 | // ALSO match property with name "12" | |
118 | assertTrue(JsonPointer.compile("/12").mayMatchProperty()); | |
90 | 119 | } |
91 | 120 | |
92 | 121 | public void testAppend() |
73 | 73 | // for [jackson-core#356] |
74 | 74 | public void testDisableSourceInclusion() throws Exception |
75 | 75 | { |
76 | JsonFactory f = new JsonFactory(); | |
77 | f.disable(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION); | |
76 | JsonFactory f = JsonFactory.builder() | |
77 | .disable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION) | |
78 | .build(); | |
78 | 79 | |
79 | 80 | JsonParser p = f.createParser("[ foobar ]"); |
80 | 81 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
16 | 16 | CharsToNameCanonicalizer.createRoot()); |
17 | 17 | assertVersion(jp.version()); |
18 | 18 | jp.close(); |
19 | JsonGenerator jgen = new WriterBasedJsonGenerator(getIOContext(), 0, null, null); | |
19 | JsonGenerator jgen = new WriterBasedJsonGenerator(getIOContext(), 0, null, null, '"'); | |
20 | 20 | assertVersion(jgen.version()); |
21 | 21 | jgen.close(); |
22 | 22 | } |
0 | 0 | package com.fasterxml.jackson.core.base64; |
1 | ||
2 | import java.util.Arrays; | |
1 | 3 | |
2 | 4 | import org.junit.Assert; |
3 | 5 | |
75 | 77 | |
76 | 78 | public void testConvenienceMethods() throws Exception |
77 | 79 | { |
78 | Base64Variant std = Base64Variants.MIME; | |
80 | final Base64Variant std = Base64Variants.MIME; | |
79 | 81 | |
80 | 82 | byte[] input = new byte[] { 1, 2, 34, 127, -1 }; |
81 | 83 | String encoded = std.encode(input, false); |
93 | 95 | Assert.assertArrayEquals(input, decoded); |
94 | 96 | decoded = std.decode(encoded + "\n"); |
95 | 97 | Assert.assertArrayEquals(input, decoded); |
98 | } | |
99 | ||
100 | public void testConvenienceMethodWithLFs() throws Exception | |
101 | { | |
102 | final Base64Variant std = Base64Variants.MIME; | |
103 | ||
104 | final int length = 100; | |
105 | final byte[] data = new byte[length]; | |
106 | Arrays.fill(data, (byte) 1); | |
107 | ||
108 | final StringBuilder sb = new StringBuilder(140); | |
109 | for (int i = 0; i < 100/3; ++i) { | |
110 | sb.append("AQEB"); | |
111 | if (sb.length() == 76) { | |
112 | sb.append("##"); | |
113 | } | |
114 | } | |
115 | sb.append("AQ=="); | |
116 | final String exp = sb.toString(); | |
117 | ||
118 | // first, JSON standard | |
119 | assertEquals(exp.replace("##", "\\n"), std.encode(data, false)); | |
120 | ||
121 | // then with custom linefeed | |
122 | ||
123 | assertEquals(exp.replace("##", "<%>"), std.encode(data, false, "<%>")); | |
96 | 124 | } |
97 | 125 | |
98 | 126 | @SuppressWarnings("unused") |
5 | 5 | import static org.junit.Assert.*; |
6 | 6 | |
7 | 7 | import com.fasterxml.jackson.core.*; |
8 | import com.fasterxml.jackson.core.util.BufferRecyclers; | |
9 | 8 | |
10 | 9 | public class TestJsonStringEncoder |
11 | 10 | extends com.fasterxml.jackson.core.BaseTest |
24 | 23 | StringBuilder output = new StringBuilder(); |
25 | 24 | StringBuilder builder = new StringBuilder(); |
26 | 25 | builder.append("foobar"); |
27 | BufferRecyclers.quoteAsJsonText(builder, output); | |
26 | JsonStringEncoder.getInstance().quoteAsString(builder, output); | |
28 | 27 | assertEquals("foobar", output.toString()); |
29 | 28 | builder.setLength(0); |
30 | 29 | output.setLength(0); |
31 | 30 | builder.append("\"x\""); |
32 | BufferRecyclers.quoteAsJsonText(builder, output); | |
31 | JsonStringEncoder.getInstance().quoteAsString(builder, output); | |
33 | 32 | assertEquals("\\\"x\\\"", output.toString()); |
34 | 33 | } |
35 | 34 | |
61 | 60 | sb2.append("\\\""); |
62 | 61 | } |
63 | 62 | String exp = sb2.toString(); |
64 | BufferRecyclers.quoteAsJsonText(input, output); | |
63 | JsonStringEncoder.getInstance().quoteAsString(input, output); | |
65 | 64 | assertEquals(2*input.length(), output.length()); |
66 | 65 | assertEquals(exp, output.toString()); |
67 | 66 | |
107 | 106 | public void testCtrlChars() throws Exception |
108 | 107 | { |
109 | 108 | char[] input = new char[] { 0, 1, 2, 3, 4 }; |
110 | char[] quoted = BufferRecyclers.quoteAsJsonText(new String(input)); | |
109 | char[] quoted = JsonStringEncoder.getInstance().quoteAsString(new String(input)); | |
111 | 110 | assertEquals("\\u0000\\u0001\\u0002\\u0003\\u0004", new String(quoted)); |
112 | 111 | } |
113 | 112 | |
118 | 117 | StringBuilder builder = new StringBuilder(); |
119 | 118 | builder.append(input); |
120 | 119 | StringBuilder output = new StringBuilder(); |
121 | BufferRecyclers.quoteAsJsonText(builder, output); | |
120 | JsonStringEncoder.getInstance().quoteAsString(builder, output); | |
122 | 121 | assertEquals("\\u0000\\u0001\\u0002\\u0003\\u0004", output.toString()); |
123 | 122 | } |
124 | 123 |
0 | package com.fasterxml.jackson.core.json; | |
1 | ||
2 | import java.io.*; | |
3 | ||
4 | import com.fasterxml.jackson.core.*; | |
5 | ||
6 | // For [core#549], ability to use alternate quote characters | |
7 | public class CustomQuoteCharTest | |
8 | extends com.fasterxml.jackson.core.BaseTest | |
9 | { | |
10 | final JsonFactory JSON_F = streamFactoryBuilder() | |
11 | .quoteChar('\'') | |
12 | .build(); | |
13 | ||
14 | // Only ASCII range supported as of 2.10 | |
15 | public void testInvalidQuote() throws Exception | |
16 | { | |
17 | try { | |
18 | streamFactoryBuilder() | |
19 | .quoteChar('\u00A0'); | |
20 | fail("Should not allow quote character outside ASCII range"); | |
21 | } catch (IllegalArgumentException e) { | |
22 | verifyException(e, "Can only use Unicode characters up to 0x7F"); | |
23 | } | |
24 | } | |
25 | ||
26 | public void testBasicAposWithCharBased() throws Exception | |
27 | { | |
28 | StringWriter w; | |
29 | JsonGenerator g; | |
30 | ||
31 | // with Object | |
32 | w = new StringWriter(); | |
33 | g = createGenerator(JSON_F, w); | |
34 | _writeObject(g, "question", "answer"); | |
35 | g.close(); | |
36 | assertEquals("{'question':'answer'}", w.toString()); | |
37 | ||
38 | // with Array | |
39 | w = new StringWriter(); | |
40 | g = createGenerator(JSON_F, w); | |
41 | _writeArray(g, "hello world"); | |
42 | g.close(); | |
43 | assertEquals("['hello world']", w.toString()); | |
44 | } | |
45 | ||
46 | public void testBasicAposWithByteBased() throws Exception | |
47 | { | |
48 | ByteArrayOutputStream out; | |
49 | JsonGenerator g; | |
50 | ||
51 | // with Object | |
52 | out = new ByteArrayOutputStream(); | |
53 | g = createGenerator(JSON_F, out); | |
54 | _writeObject(g, "question", "answer"); | |
55 | g.close(); | |
56 | assertEquals("{'question':'answer'}", out.toString("UTF-8")); | |
57 | ||
58 | // with Array | |
59 | out = new ByteArrayOutputStream(); | |
60 | g = createGenerator(JSON_F, out); | |
61 | _writeArray(g, "hello world"); | |
62 | g.close(); | |
63 | assertEquals("['hello world']", out.toString("UTF-8")); | |
64 | } | |
65 | ||
66 | public void testAposQuotingWithCharBased() throws Exception | |
67 | { | |
68 | StringWriter w; | |
69 | JsonGenerator g; | |
70 | ||
71 | // with Object | |
72 | w = new StringWriter(); | |
73 | g = createGenerator(JSON_F, w); | |
74 | _writeObject(g, "key", "It's \"fun\""); | |
75 | g.close(); | |
76 | // should escape apostrophes but not quotes? | |
77 | assertEquals("{'key':'It\\u0027s \\\"fun\\\"'}", w.toString()); | |
78 | ||
79 | // with Array | |
80 | w = new StringWriter(); | |
81 | g = createGenerator(JSON_F, w); | |
82 | _writeArray(g, "It's a sin"); | |
83 | g.close(); | |
84 | assertEquals("['It\\u0027s a sin']", w.toString()); | |
85 | } | |
86 | ||
87 | public void testAposQuotingWithByteBased() throws Exception | |
88 | { | |
89 | ByteArrayOutputStream out; | |
90 | JsonGenerator g; | |
91 | ||
92 | // with Object | |
93 | out = new ByteArrayOutputStream(); | |
94 | g = createGenerator(JSON_F, out); | |
95 | _writeObject(g, "key", "It's \"fun\""); | |
96 | g.close(); | |
97 | // should escape apostrophes but not quotes? | |
98 | assertEquals("{'key':'It\\u0027s \\\"fun\\\"'}", out.toString("UTF-8")); | |
99 | ||
100 | // with Array | |
101 | out = new ByteArrayOutputStream(); | |
102 | g = createGenerator(JSON_F, out); | |
103 | _writeArray(g, "It's a sin"); | |
104 | g.close(); | |
105 | assertEquals("['It\\u0027s a sin']", out.toString("UTF-8")); | |
106 | } | |
107 | ||
108 | private void _writeObject(JsonGenerator g, String key, String value) throws Exception { | |
109 | g.writeStartObject(); | |
110 | g.writeStringField(key, value); | |
111 | g.writeEndObject(); | |
112 | } | |
113 | ||
114 | private void _writeArray(JsonGenerator g, String value) throws Exception { | |
115 | g.writeStartArray(); | |
116 | g.writeString(value); | |
117 | g.writeEndArray(); | |
118 | } | |
119 | } |
14 | 14 | { |
15 | 15 | private final JsonFactory JSON_F = new JsonFactory(); |
16 | 16 | |
17 | @SuppressWarnings("deprecation") | |
17 | 18 | public void testConfigDefaults() throws IOException |
18 | 19 | { |
19 | 20 | JsonGenerator g = JSON_F.createGenerator(new StringWriter()); |
20 | 21 | assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); |
22 | ||
21 | 23 | assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)); |
24 | assertFalse(g.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN)); | |
22 | 25 | |
23 | 26 | assertTrue(g.canOmitFields()); |
24 | 27 | assertFalse(g.canWriteBinaryNatively()); |
39 | 42 | g.overrideStdFeatures(mask, mask); |
40 | 43 | assertTrue(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); |
41 | 44 | assertTrue(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)); |
45 | assertTrue(g.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN)); | |
42 | 46 | |
43 | 47 | // and for now, also test straight override |
44 | 48 | g.setFeatureMask(0); |
45 | 49 | assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); |
46 | 50 | assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN)); |
51 | assertFalse(g.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN)); | |
47 | 52 | g.close(); |
48 | 53 | } |
49 | 54 | |
53 | 58 | // by default, quoting should be enabled |
54 | 59 | _testFieldNameQuoting(f, true); |
55 | 60 | // can disable it |
56 | f.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES); | |
61 | f = JsonFactory.builder() | |
62 | .disable(JsonWriteFeature.QUOTE_FIELD_NAMES) | |
63 | .build(); | |
57 | 64 | _testFieldNameQuoting(f, false); |
58 | 65 | // and (re)enable: |
59 | f.enable(JsonGenerator.Feature.QUOTE_FIELD_NAMES); | |
66 | f = JsonFactory.builder() | |
67 | .enable(JsonWriteFeature.QUOTE_FIELD_NAMES) | |
68 | .build(); | |
60 | 69 | _testFieldNameQuoting(f, true); |
61 | 70 | } |
62 | 71 | |
66 | 75 | // by default, quoting should be enabled |
67 | 76 | _testNonNumericQuoting(f, true); |
68 | 77 | // can disable it |
69 | f.disable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS); | |
78 | f = JsonFactory.builder() | |
79 | .disable(JsonWriteFeature.WRITE_NAN_AS_STRINGS) | |
80 | .build(); | |
70 | 81 | _testNonNumericQuoting(f, false); |
71 | 82 | // and (re)enable: |
72 | f.enable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS); | |
83 | f = JsonFactory.builder() | |
84 | .enable(JsonWriteFeature.WRITE_NAN_AS_STRINGS) | |
85 | .build(); | |
73 | 86 | _testNonNumericQuoting(f, true); |
74 | 87 | } |
75 | 88 | |
84 | 97 | assertEquals("[1,2,1.25,2.25,3001,0.5,-1]", _writeNumbers(f)); |
85 | 98 | |
86 | 99 | // but if overridden, quotes as Strings |
87 | f.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true); | |
100 | f = JsonFactory.builder() | |
101 | .enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS) | |
102 | .build(); | |
88 | 103 | assertEquals("[\"1\",\"2\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\"]", |
89 | 104 | _writeNumbers(f)); |
90 | 105 | |
112 | 127 | |
113 | 128 | public void testBigDecimalAsPlainString() throws Exception |
114 | 129 | { |
115 | JsonFactory f = new JsonFactory(); | |
116 | 130 | BigDecimal ENG = new BigDecimal("1E+2"); |
117 | f.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); | |
118 | f.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS); | |
131 | JsonFactory f = JsonFactory.builder() | |
132 | .enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) | |
133 | .enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS) | |
134 | .build(); | |
119 | 135 | |
120 | 136 | StringWriter sw = new StringWriter(); |
121 | 137 | JsonGenerator g = f.createGenerator(sw); |
132 | 148 | } |
133 | 149 | |
134 | 150 | // [core#315] |
151 | @SuppressWarnings("deprecation") | |
135 | 152 | public void testTooBigBigDecimal() throws Exception |
136 | 153 | { |
137 | 154 | JsonFactory f = new JsonFactory(); |
220 | 237 | |
221 | 238 | // // Then with alternatively configured factory |
222 | 239 | |
223 | JsonFactory f2 = new JsonFactory(); | |
224 | f2.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES); | |
240 | JsonFactory f2 = JsonFactory.builder() | |
241 | .disable(JsonWriteFeature.QUOTE_FIELD_NAMES) | |
242 | .build(); | |
225 | 243 | |
226 | 244 | _testFieldNameQuotingEnabled(f2, true, true, "{\"foo\":1}"); |
227 | 245 | _testFieldNameQuotingEnabled(f2, false, true, "{\"foo\":1}"); |
231 | 249 | _testFieldNameQuotingEnabled(f2, false, false, "{foo:1}"); |
232 | 250 | } |
233 | 251 | |
252 | @SuppressWarnings("deprecation") | |
234 | 253 | private void _testFieldNameQuotingEnabled(JsonFactory f, boolean useBytes, |
235 | 254 | boolean useQuotes, String exp) throws IOException |
236 | 255 | { |
253 | 272 | assertEquals(exp, json); |
254 | 273 | } |
255 | 274 | |
275 | @SuppressWarnings("deprecation") | |
256 | 276 | public void testChangeOnGenerator() throws IOException |
257 | 277 | { |
258 | 278 | StringWriter w = new StringWriter(); |
19 | 19 | } |
20 | 20 | |
21 | 21 | @Override |
22 | public <T> T readValue(JsonParser p, TypeReference<?> valueTypeRef) throws IOException { | |
22 | public <T> T readValue(JsonParser p, TypeReference<T> valueTypeRef) throws IOException { | |
23 | 23 | return null; |
24 | 24 | } |
25 | 25 | |
34 | 34 | } |
35 | 35 | |
36 | 36 | @Override |
37 | public <T> Iterator<T> readValues(JsonParser p, TypeReference<?> valueTypeRef) throws IOException { | |
37 | public <T> Iterator<T> readValues(JsonParser p, TypeReference<T> valueTypeRef) throws IOException { | |
38 | 38 | return null; |
39 | 39 | } |
40 | 40 | |
73 | 73 | |
74 | 74 | @Override |
75 | 75 | public <T> T treeToValue(TreeNode n, Class<T> valueType) throws JsonProcessingException { |
76 | return null; | |
77 | } | |
78 | ||
79 | @Override | |
80 | public TreeNode missingNode() { | |
81 | return null; | |
82 | } | |
83 | ||
84 | @Override | |
85 | public TreeNode nullNode() { | |
76 | 86 | return null; |
77 | 87 | } |
78 | 88 | } |
91 | 101 | /********************************************************************** |
92 | 102 | */ |
93 | 103 | |
104 | @SuppressWarnings("deprecation") | |
94 | 105 | public void testGeneratorFeatures() throws Exception |
95 | 106 | { |
96 | 107 | JsonFactory f = new JsonFactory(); |
97 | 108 | assertNull(f.getCodec()); |
98 | 109 | |
99 | f.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true); | |
110 | f = JsonFactory.builder() | |
111 | .configure(JsonWriteFeature.QUOTE_FIELD_NAMES, true) | |
112 | .build(); | |
113 | // 24-Oct-2018, tatu: Until 3.x, we'll only have backwards compatible | |
100 | 114 | assertTrue(f.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); |
101 | f.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false); | |
115 | f = JsonFactory.builder() | |
116 | .configure(JsonWriteFeature.QUOTE_FIELD_NAMES, false) | |
117 | .build(); | |
102 | 118 | assertFalse(f.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); |
103 | 119 | } |
104 | 120 | |
105 | 121 | public void testFactoryFeatures() throws Exception |
106 | 122 | { |
107 | JsonFactory f = new JsonFactory(); | |
108 | ||
109 | f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, true); | |
110 | assertTrue(f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); | |
111 | f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, false); | |
123 | JsonFactory f = JsonFactory.builder() | |
124 | .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, false) | |
125 | .build(); | |
112 | 126 | assertFalse(f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); |
113 | 127 | |
114 | 128 | // by default, should be enabled |
115 | 129 | assertTrue(f.isEnabled(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING)); |
116 | f.configure(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING, false); | |
117 | assertFalse(f.isEnabled(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING)); | |
118 | 130 | } |
119 | 131 | |
120 | 132 | // for [core#189]: verify that it's ok to disable recycling |
123 | 135 | // disables this handling otherwise |
124 | 136 | public void testDisablingBufferRecycling() throws Exception |
125 | 137 | { |
126 | JsonFactory f = new JsonFactory(); | |
127 | ||
128 | f.disable(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING); | |
138 | JsonFactory f = JsonFactory.builder() | |
139 | .disable(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING) | |
140 | .build(); | |
141 | assertFalse(f.isEnabled(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING)); | |
129 | 142 | |
130 | 143 | // First, generation |
131 | 144 | for (int i = 0; i < 3; ++i) { |
195 | 208 | } |
196 | 209 | |
197 | 210 | // #72 |
211 | @SuppressWarnings("deprecation") | |
198 | 212 | public void testCopy() throws Exception |
199 | 213 | { |
200 | 214 | JsonFactory jf = new JsonFactory(); |
205 | 219 | assertFalse(jf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII)); |
206 | 220 | |
207 | 221 | // then change, verify that changes "stick" |
208 | jf.disable(JsonFactory.Feature.INTERN_FIELD_NAMES); | |
209 | jf.enable(JsonParser.Feature.ALLOW_COMMENTS); | |
210 | jf.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII); | |
222 | jf = JsonFactory.builder() | |
223 | .disable(JsonFactory.Feature.INTERN_FIELD_NAMES) | |
224 | .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | |
225 | .enable(JsonWriteFeature.ESCAPE_NON_ASCII) | |
226 | .build(); | |
211 | 227 | ObjectCodec codec = new BogusCodec(); |
212 | 228 | jf.setCodec(codec); |
213 | 229 |
0 | package com.fasterxml.jackson.core.json; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | // Tests mostly for [core#229] | |
5 | public class LocationInArrayTest extends com.fasterxml.jackson.core.BaseTest | |
6 | { | |
7 | final JsonFactory JSON_F = new JsonFactory(); | |
8 | ||
9 | // for [core#229] | |
10 | public void testOffsetInArraysBytes() throws Exception { | |
11 | _testOffsetInArrays(true); | |
12 | } | |
13 | ||
14 | // for [core#229] | |
15 | public void testOffsetInArraysChars() throws Exception { | |
16 | _testOffsetInArrays(false); | |
17 | } | |
18 | ||
19 | private void _testOffsetInArrays(boolean useBytes) throws Exception | |
20 | { | |
21 | JsonParser p; | |
22 | final String DOC = " [10, 251,\n 3 ]"; | |
23 | ||
24 | // first, char based: | |
25 | p = useBytes ? JSON_F.createParser(DOC.getBytes("UTF-8")) | |
26 | : JSON_F.createParser(DOC.toCharArray()); | |
27 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
28 | _assertLocation(useBytes, p.getTokenLocation(), 2L, 1, 3); | |
29 | _assertLocation(useBytes, p.getCurrentLocation(), 3L, 1, 4); | |
30 | ||
31 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
32 | _assertLocation(useBytes, p.getTokenLocation(), 3L, 1, 4); | |
33 | assertEquals(10, p.getIntValue()); // just to ensure read proceeds to end | |
34 | // 2-digits so | |
35 | _assertLocation(useBytes, p.getCurrentLocation(), 5L, 1, 6); | |
36 | ||
37 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
38 | _assertLocation(useBytes, p.getTokenLocation(), 7L, 1, 8); | |
39 | assertEquals(251, p.getIntValue()); // just to ensure read proceeds to end | |
40 | _assertLocation(useBytes, p.getCurrentLocation(), 10L, 1, 11); | |
41 | ||
42 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
43 | _assertLocation(useBytes, p.getTokenLocation(), 15L, 2, 4); | |
44 | assertEquals(3, p.getIntValue()); | |
45 | _assertLocation(useBytes, p.getCurrentLocation(), 16L, 2, 5); | |
46 | ||
47 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
48 | _assertLocation(useBytes, p.getTokenLocation(), 18L, 2, 7); | |
49 | _assertLocation(useBytes, p.getCurrentLocation(), 19L, 2, 8); | |
50 | ||
51 | p.close(); | |
52 | } | |
53 | ||
54 | private void _assertLocation(boolean useBytes, JsonLocation loc, long offset, int row, int col) | |
55 | { | |
56 | assertEquals(row, loc.getLineNr()); | |
57 | assertEquals(col, loc.getColumnNr()); | |
58 | ||
59 | if (useBytes) { | |
60 | assertEquals(offset, loc.getByteOffset()); | |
61 | } else { | |
62 | assertEquals(offset, loc.getCharOffset()); | |
63 | } | |
64 | } | |
65 | } |
0 | package com.fasterxml.jackson.core.json; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | // tests for [core#37] | |
5 | public class LocationInObjectTest extends BaseTest | |
6 | { | |
7 | public void testOffsetWithObjectFieldsUsingUTF8() throws Exception | |
8 | { | |
9 | final JsonFactory f = new JsonFactory(); | |
10 | byte[] b = "{\"f1\":\"v1\",\"f2\":{\"f3\":\"v3\"},\"f4\":[true,false],\"f5\":5}".getBytes("UTF-8"); | |
11 | // 1 6 11 16 17 22 28 33 34 39 46 51 | |
12 | JsonParser p = f.createParser(b); | |
13 | ||
14 | assertEquals(JsonToken.START_OBJECT, p.nextToken()); | |
15 | ||
16 | assertEquals(JsonToken.FIELD_NAME, p.nextToken()); | |
17 | assertEquals(1L, p.getTokenLocation().getByteOffset()); | |
18 | assertEquals(JsonToken.VALUE_STRING, p.nextToken()); | |
19 | assertEquals(6L, p.getTokenLocation().getByteOffset()); | |
20 | ||
21 | assertEquals("f2", p.nextFieldName()); | |
22 | assertEquals(11L, p.getTokenLocation().getByteOffset()); | |
23 | assertEquals(JsonToken.START_OBJECT, p.nextValue()); | |
24 | assertEquals(16L, p.getTokenLocation().getByteOffset()); | |
25 | ||
26 | assertEquals("f3", p.nextFieldName()); | |
27 | assertEquals(17L, p.getTokenLocation().getByteOffset()); | |
28 | assertEquals(JsonToken.VALUE_STRING, p.nextValue()); | |
29 | assertEquals(22L, p.getTokenLocation().getByteOffset()); | |
30 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
31 | ||
32 | assertEquals("f4", p.nextFieldName()); | |
33 | assertEquals(28L, p.getTokenLocation().getByteOffset()); | |
34 | assertEquals(JsonToken.START_ARRAY, p.nextValue()); | |
35 | assertEquals(33L, p.getTokenLocation().getByteOffset()); | |
36 | ||
37 | assertEquals(JsonToken.VALUE_TRUE, p.nextValue()); | |
38 | assertEquals(34L, p.getTokenLocation().getByteOffset()); | |
39 | ||
40 | assertEquals(JsonToken.VALUE_FALSE, p.nextValue()); | |
41 | assertEquals(39L, p.getTokenLocation().getByteOffset()); | |
42 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); | |
43 | ||
44 | assertEquals("f5", p.nextFieldName()); | |
45 | assertEquals(46L, p.getTokenLocation().getByteOffset()); | |
46 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
47 | assertEquals(51L, p.getTokenLocation().getByteOffset()); | |
48 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
49 | ||
50 | p.close(); | |
51 | } | |
52 | ||
53 | public void testOffsetWithObjectFieldsUsingReader() throws Exception | |
54 | { | |
55 | final JsonFactory f = new JsonFactory(); | |
56 | char[] c = "{\"f1\":\"v1\",\"f2\":{\"f3\":\"v3\"},\"f4\":[true,false],\"f5\":5}".toCharArray(); | |
57 | // 1 6 11 16 17 22 28 33 34 39 46 51 | |
58 | JsonParser p = f.createParser(c); | |
59 | ||
60 | assertEquals(JsonToken.START_OBJECT, p.nextToken()); | |
61 | ||
62 | assertEquals(JsonToken.FIELD_NAME, p.nextToken()); | |
63 | assertEquals(1L, p.getTokenLocation().getCharOffset()); | |
64 | assertEquals(JsonToken.VALUE_STRING, p.nextToken()); | |
65 | assertEquals(6L, p.getTokenLocation().getCharOffset()); | |
66 | ||
67 | assertEquals("f2", p.nextFieldName()); | |
68 | assertEquals(11L, p.getTokenLocation().getCharOffset()); | |
69 | assertEquals(JsonToken.START_OBJECT, p.nextValue()); | |
70 | assertEquals(16L, p.getTokenLocation().getCharOffset()); | |
71 | ||
72 | assertEquals("f3", p.nextFieldName()); | |
73 | assertEquals(17L, p.getTokenLocation().getCharOffset()); | |
74 | assertEquals(JsonToken.VALUE_STRING, p.nextValue()); | |
75 | assertEquals(22L, p.getTokenLocation().getCharOffset()); | |
76 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
77 | ||
78 | assertEquals("f4", p.nextFieldName()); | |
79 | assertEquals(28L, p.getTokenLocation().getCharOffset()); | |
80 | assertEquals(JsonToken.START_ARRAY, p.nextValue()); | |
81 | assertEquals(33L, p.getTokenLocation().getCharOffset()); | |
82 | ||
83 | assertEquals(JsonToken.VALUE_TRUE, p.nextValue()); | |
84 | assertEquals(34L, p.getTokenLocation().getCharOffset()); | |
85 | ||
86 | assertEquals(JsonToken.VALUE_FALSE, p.nextValue()); | |
87 | assertEquals(39L, p.getTokenLocation().getCharOffset()); | |
88 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); | |
89 | ||
90 | assertEquals("f5", p.nextFieldName()); | |
91 | assertEquals(46L, p.getTokenLocation().getCharOffset()); | |
92 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
93 | assertEquals(51L, p.getTokenLocation().getCharOffset()); | |
94 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
95 | ||
96 | p.close(); | |
97 | } | |
98 | } |
0 | package com.fasterxml.jackson.core.json; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | // NOTE: just a stub so for, fill me! | |
5 | public class LocationOffsetsTest extends com.fasterxml.jackson.core.BaseTest | |
6 | { | |
7 | final JsonFactory JSON_F = new JsonFactory(); | |
8 | ||
9 | // Trivially simple unit test for basics wrt offsets | |
10 | public void testSimpleInitialOffsets() throws Exception | |
11 | { | |
12 | JsonLocation loc; | |
13 | JsonParser p; | |
14 | final String DOC = "{ }"; | |
15 | ||
16 | // first, char based: | |
17 | p = JSON_F.createParser(DOC); | |
18 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
19 | ||
20 | loc = p.getTokenLocation(); | |
21 | assertEquals(-1L, loc.getByteOffset()); | |
22 | assertEquals(0L, loc.getCharOffset()); | |
23 | assertEquals(1, loc.getLineNr()); | |
24 | assertEquals(1, loc.getColumnNr()); | |
25 | ||
26 | loc = p.getCurrentLocation(); | |
27 | assertEquals(-1L, loc.getByteOffset()); | |
28 | assertEquals(1L, loc.getCharOffset()); | |
29 | assertEquals(1, loc.getLineNr()); | |
30 | assertEquals(2, loc.getColumnNr()); | |
31 | ||
32 | p.close(); | |
33 | ||
34 | // then byte-based | |
35 | ||
36 | p = JSON_F.createParser(DOC.getBytes("UTF-8")); | |
37 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
38 | ||
39 | loc = p.getTokenLocation(); | |
40 | assertEquals(0L, loc.getByteOffset()); | |
41 | assertEquals(-1L, loc.getCharOffset()); | |
42 | assertEquals(1, loc.getLineNr()); | |
43 | assertEquals(1, loc.getColumnNr()); | |
44 | ||
45 | loc = p.getCurrentLocation(); | |
46 | assertEquals(1L, loc.getByteOffset()); | |
47 | assertEquals(-1L, loc.getCharOffset()); | |
48 | assertEquals(1, loc.getLineNr()); | |
49 | assertEquals(2, loc.getColumnNr()); | |
50 | ||
51 | p.close(); | |
52 | } | |
53 | ||
54 | // for [core#111] | |
55 | public void testOffsetWithInputOffset() throws Exception | |
56 | { | |
57 | JsonLocation loc; | |
58 | JsonParser p; | |
59 | // 3 spaces before, 2 after, just for padding | |
60 | byte[] b = " { } ".getBytes("UTF-8"); | |
61 | ||
62 | // and then peel them off | |
63 | p = JSON_F.createParser(b, 3, b.length-5); | |
64 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
65 | ||
66 | loc = p.getTokenLocation(); | |
67 | assertEquals(0L, loc.getByteOffset()); | |
68 | assertEquals(-1L, loc.getCharOffset()); | |
69 | assertEquals(1, loc.getLineNr()); | |
70 | assertEquals(1, loc.getColumnNr()); | |
71 | ||
72 | loc = p.getCurrentLocation(); | |
73 | assertEquals(1L, loc.getByteOffset()); | |
74 | assertEquals(-1L, loc.getCharOffset()); | |
75 | assertEquals(1, loc.getLineNr()); | |
76 | assertEquals(2, loc.getColumnNr()); | |
77 | ||
78 | p.close(); | |
79 | } | |
80 | } |
1 | 1 | |
2 | 2 | import com.fasterxml.jackson.core.*; |
3 | 3 | |
4 | import java.io.ByteArrayOutputStream; | |
5 | import java.io.StringReader; | |
6 | import java.io.StringWriter; | |
4 | import java.io.*; | |
7 | 5 | import java.util.Random; |
8 | 6 | |
9 | 7 | /** |
273 | 271 | assertEquals(JsonToken.END_ARRAY, p.currentToken()); |
274 | 272 | p.close(); |
275 | 273 | } |
274 | ||
275 | // [jackson-core#556] | |
276 | public void testIssue556() throws Exception | |
277 | { | |
278 | StringBuilder sb = new StringBuilder(8000); | |
279 | sb.append('"'); | |
280 | for (int i = 0; i < 7988; i++) { | |
281 | sb.append("a"); | |
282 | } | |
283 | sb.append('"'); | |
284 | JsonGenerator g = FACTORY.createGenerator(new ByteArrayOutputStream()); | |
285 | ||
286 | g.writeStartArray(); | |
287 | _write556(g, sb.toString()); | |
288 | _write556(g, "b"); | |
289 | _write556(g, "c"); | |
290 | g.writeEndArray(); | |
291 | g.close(); | |
292 | } | |
293 | ||
294 | private void _write556(JsonGenerator g, String value) throws Exception | |
295 | { | |
296 | g.writeString(new StringReader(value), -1); | |
297 | } | |
276 | 298 | } |
11 | 11 | public class TestCharEscaping |
12 | 12 | extends com.fasterxml.jackson.core.BaseTest |
13 | 13 | { |
14 | // for [JACKSON-627] | |
15 | 14 | @SuppressWarnings("serial") |
16 | 15 | private final static CharacterEscapes ESC_627 = new CharacterEscapes() { |
17 | 16 | final int[] ascii = CharacterEscapes.standardAsciiEscapesForJSON(); |
36 | 35 | /* Unit tests |
37 | 36 | /********************************************************** |
38 | 37 | */ |
38 | ||
39 | private final static JsonFactory JSON_F = new JsonFactory(); | |
39 | 40 | |
40 | 41 | public void testMissingEscaping() |
41 | 42 | throws Exception |
131 | 132 | jp.close(); |
132 | 133 | } |
133 | 134 | |
134 | // for [JACKSON-627] | |
135 | 135 | public void testWriteLongCustomEscapes() throws Exception |
136 | 136 | { |
137 | 137 | JsonFactory jf = new JsonFactory(); |
149 | 149 | jgen.close(); |
150 | 150 | } |
151 | 151 | |
152 | // [Issue#116] | |
152 | // [jackson-core#116] | |
153 | 153 | public void testEscapesForCharArrays() throws Exception { |
154 | JsonFactory jf = new JsonFactory(); | |
155 | 154 | StringWriter writer = new StringWriter(); |
156 | JsonGenerator jgen = jf.createGenerator(writer); | |
155 | JsonGenerator jgen = JSON_F.createGenerator(writer); | |
157 | 156 | // must call #writeString(char[],int,int) and not #writeString(String) |
158 | 157 | jgen.writeString(new char[] { '\0' }, 0, 1); |
159 | 158 | jgen.close(); |
160 | 159 | assertEquals("\"\\u0000\"", writer.toString()); |
161 | 160 | } |
161 | ||
162 | // [jackson-core#540] | |
163 | public void testInvalidEscape() throws Exception { | |
164 | JsonParser p = JSON_F.createParser(quote("\\u\u0080...").getBytes("UTF-8")); | |
165 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
166 | // this is where we should get proper exception | |
167 | try { | |
168 | p.getText(); | |
169 | fail("Should not pass"); | |
170 | } catch (JsonParseException e) { | |
171 | verifyException(e, "Unexpected character"); | |
172 | } | |
173 | p.close(); | |
174 | } | |
175 | ||
176 | // [jackson-core#116] | |
177 | public void testEscapeNonLatin1Chars() throws Exception { | |
178 | _testEscapeNonLatin1ViaChars(false); | |
179 | } | |
180 | ||
181 | // [jackson-core#116] | |
182 | public void testEscapeNonLatin1Bytes() throws Exception { | |
183 | _testEscapeNonLatin1ViaChars(true); | |
184 | } | |
185 | ||
186 | private void _testEscapeNonLatin1ViaChars(boolean useBytes) throws Exception { | |
187 | // NOTE! First one is outside latin-1, so escape; second one within, do NOT escape: | |
188 | final String VALUE_IN = "Line\u2028feed, \u00D6l!"; | |
189 | final String VALUE_ESCAPED = "Line\\u2028feed, \u00D6l!"; | |
190 | final JsonFactory DEFAULT_F = new JsonFactory(); | |
191 | ||
192 | // First: with default settings, no auto-escaping | |
193 | _testEscapeNonLatin1(DEFAULT_F, VALUE_IN, VALUE_IN, useBytes); // char | |
194 | ||
195 | // Second: with escaping beyond Latin-1 range | |
196 | final JsonFactory latinF = ((JsonFactoryBuilder)JsonFactory.builder()) | |
197 | .highestNonEscapedChar(255) | |
198 | .build(); | |
199 | _testEscapeNonLatin1(latinF, VALUE_IN, VALUE_ESCAPED, useBytes); | |
200 | } | |
201 | ||
202 | private void _testEscapeNonLatin1(JsonFactory f, String valueIn, String expEncoded, | |
203 | boolean useBytes) throws Exception | |
204 | { | |
205 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(); | |
206 | StringWriter sw = new StringWriter(); | |
207 | final JsonGenerator g = useBytes ? f.createGenerator(bytes, JsonEncoding.UTF8) | |
208 | : f.createGenerator(sw); | |
209 | g.writeStartArray(); | |
210 | g.writeString(valueIn); | |
211 | g.writeEndArray(); | |
212 | g.close(); | |
213 | ||
214 | // Don't parse, as we want to verify actual escaping aspects | |
215 | ||
216 | final String doc = useBytes ? bytes.toString("UTF-8") : sw.toString(); | |
217 | assertEquals("[\""+expEncoded+"\"]", doc); | |
218 | } | |
162 | 219 | } |
163 |
127 | 127 | /******************************************************** |
128 | 128 | */ |
129 | 129 | |
130 | @SuppressWarnings("resource") | |
130 | @SuppressWarnings({ "resource", "deprecation" }) | |
131 | 131 | private void _testEscapeAboveAscii(boolean useStream) throws Exception |
132 | 132 | { |
133 | 133 | JsonFactory f = new JsonFactory(); |
70 | 70 | |
71 | 71 | public void testInputDecoration() throws IOException |
72 | 72 | { |
73 | JsonFactory f = new JsonFactory(); | |
74 | f.setInputDecorator(new SimpleInputDecorator()); | |
75 | JsonParser jp; | |
73 | JsonFactory f = JsonFactory.builder() | |
74 | .inputDecorator(new SimpleInputDecorator()) | |
75 | .build(); | |
76 | JsonParser p; | |
76 | 77 | // first test with Reader |
77 | jp = f.createParser(new StringReader("{ }")); | |
78 | p = f.createParser(new StringReader("{ }")); | |
78 | 79 | // should be overridden; |
79 | assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); | |
80 | assertEquals(789, jp.getIntValue()); | |
81 | jp.close(); | |
80 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
81 | assertEquals(789, p.getIntValue()); | |
82 | p.close(); | |
82 | 83 | |
83 | 84 | // similarly with InputStream |
84 | jp = f.createParser(new ByteArrayInputStream("[ ]".getBytes("UTF-8"))); | |
85 | p = f.createParser(new ByteArrayInputStream("[ ]".getBytes("UTF-8"))); | |
85 | 86 | // should be overridden; |
86 | assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); | |
87 | assertEquals(123, jp.getIntValue()); | |
88 | jp.close(); | |
87 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
88 | assertEquals(123, p.getIntValue()); | |
89 | p.close(); | |
89 | 90 | |
90 | 91 | // and with raw bytes |
91 | jp = f.createParser("[ ]".getBytes("UTF-8")); | |
92 | p = f.createParser("[ ]".getBytes("UTF-8")); | |
92 | 93 | // should be overridden; |
93 | assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); | |
94 | assertEquals(456, jp.getIntValue()); | |
95 | jp.close(); | |
94 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
95 | assertEquals(456, p.getIntValue()); | |
96 | p.close(); | |
96 | 97 | } |
97 | 98 | |
98 | 99 | public void testOutputDecoration() throws IOException |
99 | 100 | { |
100 | JsonFactory f = new JsonFactory(); | |
101 | f.setOutputDecorator(new SimpleOutputDecorator()); | |
102 | JsonGenerator jg; | |
101 | JsonFactory f = JsonFactory.builder() | |
102 | .outputDecorator(new SimpleOutputDecorator()) | |
103 | .build(); | |
104 | JsonGenerator g; | |
103 | 105 | |
104 | 106 | StringWriter sw = new StringWriter(); |
105 | jg = f.createGenerator(sw); | |
106 | jg.close(); | |
107 | g = f.createGenerator(sw); | |
108 | g.close(); | |
107 | 109 | assertEquals("567", sw.toString()); |
108 | 110 | |
109 | 111 | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
110 | jg = f.createGenerator(out, JsonEncoding.UTF8); | |
111 | jg.close(); | |
112 | g = f.createGenerator(out, JsonEncoding.UTF8); | |
113 | g.close(); | |
112 | 114 | assertEquals("123", out.toString("UTF-8")); |
113 | 115 | } |
114 | 116 | } |
+140
-51
0 | 0 | package com.fasterxml.jackson.core.json; |
1 | 1 | |
2 | 2 | import java.io.*; |
3 | import java.util.Random; | |
3 | 4 | |
4 | 5 | import com.fasterxml.jackson.core.*; |
5 | 6 | import com.fasterxml.jackson.core.io.SerializedString; |
10 | 11 | final static String NAME_WITH_QUOTES = "\"name\""; |
11 | 12 | final static String NAME_WITH_LATIN1 = "P\u00f6ll\u00f6"; |
12 | 13 | |
14 | final static String VALUE_WITH_QUOTES = "\"Value\""; | |
15 | final static String VALUE2 = _generateLongName(9000); | |
16 | ||
17 | private final JsonFactory JSON_F = new JsonFactory(); | |
18 | ||
13 | 19 | private final SerializedString quotedName = new SerializedString(NAME_WITH_QUOTES); |
14 | 20 | private final SerializedString latin1Name = new SerializedString(NAME_WITH_LATIN1); |
15 | 21 | |
16 | public void testSimple() throws Exception | |
22 | public void testSimpleFieldNames() throws Exception | |
17 | 23 | { |
18 | JsonFactory jf = new JsonFactory(); | |
19 | ||
20 | 24 | // First using char-backed generator |
21 | 25 | StringWriter sw = new StringWriter(); |
22 | JsonGenerator jgen = jf.createGenerator(sw); | |
23 | _writeSimple(jgen); | |
24 | jgen.close(); | |
26 | JsonGenerator gen = JSON_F.createGenerator(sw); | |
27 | _writeSimple(gen); | |
28 | gen.close(); | |
25 | 29 | String json = sw.toString(); |
26 | _verifySimple(jf.createParser(json)); | |
30 | _verifySimple(JSON_F.createParser(json)); | |
27 | 31 | |
28 | 32 | // then using UTF-8 |
29 | 33 | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
30 | jgen = jf.createGenerator(out, JsonEncoding.UTF8); | |
31 | _writeSimple(jgen); | |
32 | jgen.close(); | |
34 | gen = JSON_F.createGenerator(out, JsonEncoding.UTF8); | |
35 | _writeSimple(gen); | |
36 | gen.close(); | |
33 | 37 | byte[] jsonB = out.toByteArray(); |
34 | _verifySimple(jf.createParser(jsonB)); | |
38 | _verifySimple(JSON_F.createParser(jsonB)); | |
39 | } | |
40 | ||
41 | public void testSimpleValues() throws Exception | |
42 | { | |
43 | // First using char-backed generator | |
44 | StringWriter sw = new StringWriter(); | |
45 | JsonGenerator gen = JSON_F.createGenerator(sw); | |
46 | _writeSimpleValues(gen); | |
47 | gen.close(); | |
48 | _verifySimpleValues(JSON_F.createParser(new StringReader(sw.toString()))); | |
49 | ||
50 | // then using UTF-8 | |
51 | ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
52 | gen = JSON_F.createGenerator(out, JsonEncoding.UTF8); | |
53 | _writeSimpleValues(gen); | |
54 | gen.close(); | |
55 | _verifySimpleValues(JSON_F.createParser(new ByteArrayInputStream(out.toByteArray()))); | |
35 | 56 | } |
36 | 57 | |
37 | 58 | /* |
39 | 60 | /* Helper methods |
40 | 61 | /********************************************************** |
41 | 62 | */ |
42 | ||
43 | private void _writeSimple(JsonGenerator jgen) throws Exception | |
63 | ||
64 | private void _writeSimple(JsonGenerator gen) throws Exception | |
44 | 65 | { |
45 | 66 | // Let's just write array of 2 objects |
46 | jgen.writeStartArray(); | |
67 | gen.writeStartArray(); | |
47 | 68 | |
48 | jgen.writeStartObject(); | |
49 | jgen.writeFieldName(quotedName); | |
50 | jgen.writeString("a"); | |
51 | jgen.writeFieldName(latin1Name); | |
52 | jgen.writeString("b"); | |
53 | jgen.writeEndObject(); | |
69 | gen.writeStartObject(); | |
70 | gen.writeFieldName(quotedName); | |
71 | gen.writeString("a"); | |
72 | gen.writeFieldName(latin1Name); | |
73 | gen.writeString("b"); | |
74 | gen.writeEndObject(); | |
54 | 75 | |
55 | jgen.writeStartObject(); | |
56 | jgen.writeFieldName(latin1Name); | |
57 | jgen.writeString("c"); | |
58 | jgen.writeFieldName(quotedName); | |
59 | jgen.writeString("d"); | |
60 | jgen.writeEndObject(); | |
76 | gen.writeStartObject(); | |
77 | gen.writeFieldName(latin1Name); | |
78 | gen.writeString("c"); | |
79 | gen.writeFieldName(quotedName); | |
80 | gen.writeString("d"); | |
81 | gen.writeEndObject(); | |
61 | 82 | |
62 | jgen.writeEndArray(); | |
83 | gen.writeEndArray(); | |
63 | 84 | } |
64 | 85 | |
65 | private void _verifySimple(JsonParser jp) throws Exception | |
86 | private void _writeSimpleValues(JsonGenerator gen) throws Exception | |
66 | 87 | { |
67 | assertToken(JsonToken.START_ARRAY, jp.nextToken()); | |
88 | // Let's just write an array of 2 objects | |
89 | gen.writeStartArray(); | |
90 | gen.writeStartObject(); | |
91 | gen.writeFieldName(NAME_WITH_QUOTES); | |
92 | gen.writeString(new SerializedString(VALUE_WITH_QUOTES)); | |
93 | gen.writeFieldName(NAME_WITH_LATIN1); | |
94 | gen.writeString(VALUE2); | |
95 | gen.writeEndObject(); | |
68 | 96 | |
69 | assertToken(JsonToken.START_OBJECT, jp.nextToken()); | |
70 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); | |
71 | assertEquals(NAME_WITH_QUOTES, jp.getText()); | |
72 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); | |
73 | assertEquals("a", jp.getText()); | |
74 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); | |
75 | assertEquals(NAME_WITH_LATIN1, jp.getText()); | |
76 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); | |
77 | assertEquals("b", jp.getText()); | |
78 | assertToken(JsonToken.END_OBJECT, jp.nextToken()); | |
97 | gen.writeStartObject(); | |
98 | gen.writeFieldName(NAME_WITH_LATIN1); | |
99 | gen.writeString(VALUE_WITH_QUOTES); | |
100 | gen.writeFieldName(NAME_WITH_QUOTES); | |
101 | gen.writeString(new SerializedString(VALUE2)); | |
102 | gen.writeEndObject(); | |
79 | 103 | |
80 | assertToken(JsonToken.START_OBJECT, jp.nextToken()); | |
81 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); | |
82 | assertEquals(NAME_WITH_LATIN1, jp.getText()); | |
83 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); | |
84 | assertEquals("c", jp.getText()); | |
85 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); | |
86 | assertEquals(NAME_WITH_QUOTES, jp.getText()); | |
87 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); | |
88 | assertEquals("d", jp.getText()); | |
89 | assertToken(JsonToken.END_OBJECT, jp.nextToken()); | |
104 | gen.writeEndArray(); | |
105 | } | |
106 | ||
107 | private void _verifySimple(JsonParser p) throws Exception | |
108 | { | |
109 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
110 | ||
111 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
112 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
113 | assertEquals(NAME_WITH_QUOTES, p.getText()); | |
114 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
115 | assertEquals("a", p.getText()); | |
116 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
117 | assertEquals(NAME_WITH_LATIN1, p.getText()); | |
118 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
119 | assertEquals("b", p.getText()); | |
120 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
121 | ||
122 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
123 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
124 | assertEquals(NAME_WITH_LATIN1, p.getText()); | |
125 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
126 | assertEquals("c", p.getText()); | |
127 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
128 | assertEquals(NAME_WITH_QUOTES, p.getText()); | |
129 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
130 | assertEquals("d", p.getText()); | |
131 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
90 | 132 | |
91 | assertToken(JsonToken.END_ARRAY, jp.nextToken()); | |
92 | assertNull(jp.nextToken()); | |
133 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
134 | assertNull(p.nextToken()); | |
135 | } | |
136 | ||
137 | private void _verifySimpleValues(JsonParser p) throws Exception | |
138 | { | |
139 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
140 | ||
141 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
142 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
143 | assertEquals(NAME_WITH_QUOTES, p.getText()); | |
144 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
145 | assertEquals(VALUE_WITH_QUOTES, p.getText()); | |
146 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
147 | assertEquals(NAME_WITH_LATIN1, p.getText()); | |
148 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
149 | assertEquals(VALUE2, p.getText()); | |
150 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
151 | ||
152 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
153 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
154 | assertEquals(NAME_WITH_LATIN1, p.getText()); | |
155 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
156 | assertEquals(VALUE_WITH_QUOTES, p.getText()); | |
157 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
158 | assertEquals(NAME_WITH_QUOTES, p.getText()); | |
159 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
160 | assertEquals(VALUE2, p.getText()); | |
161 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
162 | ||
163 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
164 | assertNull(p.nextToken()); | |
165 | } | |
166 | ||
167 | private static String _generateLongName(int minLen) | |
168 | { | |
169 | StringBuilder sb = new StringBuilder(); | |
170 | Random rnd = new Random(123); | |
171 | while (sb.length() < minLen) { | |
172 | int ch = rnd.nextInt(96); | |
173 | if (ch < 32) { // ascii (single byte) | |
174 | sb.append((char) (48 + ch)); | |
175 | } else if (ch < 64) { // 2 byte | |
176 | sb.append((char) (128 + ch)); | |
177 | } else { // 3 byte | |
178 | sb.append((char) (4000 + ch)); | |
179 | } | |
180 | } | |
181 | return sb.toString(); | |
93 | 182 | } |
94 | 183 | } |
19 | 19 | |
20 | 20 | public void testLongErrorMessageReader() throws Exception |
21 | 21 | { |
22 | _testLongErrorMessage(MODE_READER); | |
22 | _testLongErrorMessage(MODE_READER); | |
23 | 23 | } |
24 | 24 | |
25 | 25 | private void _testLongErrorMessage(int mode) throws Exception |
35 | 35 | fail("Expected an exception for unrecognized token"); |
36 | 36 | } catch (JsonParseException jpe) { |
37 | 37 | String msg = jpe.getMessage(); |
38 | final String expectedPrefix = "Unrecognized token '"; | |
39 | final String expectedSuffix = "...': was expecting ('true', 'false' or 'null')"; | |
40 | assertTrue(msg.startsWith(expectedPrefix)); | |
38 | final String expectedPrefix = "Unrecognized token '"; | |
39 | final String expectedSuffix = "...': was expecting"; | |
40 | verifyException(jpe, expectedPrefix); | |
41 | verifyException(jpe, expectedSuffix); | |
41 | 42 | assertTrue(msg.contains(expectedSuffix)); |
42 | 43 | int tokenLen = msg.indexOf (expectedSuffix) - expectedPrefix.length(); |
43 | 44 | assertEquals(EXPECTED_MAX_TOKEN_LEN, tokenLen); |
64 | 65 | } catch (JsonParseException jpe) { |
65 | 66 | String msg = jpe.getMessage(); |
66 | 67 | final String expectedPrefix = "Unrecognized token '"; |
67 | final String expectedSuffix = "': was expecting ('true', 'false' or 'null')"; | |
68 | assertTrue(msg.startsWith(expectedPrefix)); | |
69 | assertTrue(msg.contains(expectedSuffix)); | |
70 | int tokenLen = msg.indexOf (expectedSuffix) - expectedPrefix.length(); | |
68 | final String expectedSuffix = "': was expecting"; | |
69 | verifyException(jpe, expectedPrefix); | |
70 | verifyException(jpe, expectedSuffix); | |
71 | int tokenLen = msg.indexOf(expectedSuffix) - expectedPrefix.length(); | |
71 | 72 | assertEquals(DOC.length(), tokenLen); |
72 | 73 | } |
73 | 74 | jp.close(); |
0 | 0 | package com.fasterxml.jackson.core.json; |
1 | 1 | |
2 | import com.fasterxml.jackson.core.BaseTest; | |
3 | import com.fasterxml.jackson.core.JsonFactory; | |
4 | import com.fasterxml.jackson.core.JsonGenerator; | |
5 | import com.fasterxml.jackson.core.JsonParser; | |
6 | import com.fasterxml.jackson.core.JsonToken; | |
2 | import com.fasterxml.jackson.core.*; | |
7 | 3 | import com.fasterxml.jackson.core.filter.FilteringGeneratorDelegate; |
8 | 4 | import com.fasterxml.jackson.core.filter.JsonPointerBasedFilter; |
9 | 5 | import com.fasterxml.jackson.core.io.IOContext; |
19 | 15 | { |
20 | 16 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(); |
21 | 17 | IOContext ioc = new IOContext(new BufferRecycler(), bytes, true); |
22 | JsonGenerator gen = new UTF8JsonGenerator(ioc, 0, null, bytes); | |
18 | JsonGenerator gen = new UTF8JsonGenerator(ioc, 0, null, bytes, '"'); | |
23 | 19 | String str = "Natuurlijk is alles gelukt en weer een tevreden klant\uD83D\uDE04"; |
24 | 20 | int length = 4000 - 38; |
25 | 21 |
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | 5 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
6 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
6 | 7 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
7 | 8 | |
8 | 9 | /** |
55 | 56 | |
56 | 57 | public void testYAMLCommentsEnabled() throws Exception |
57 | 58 | { |
58 | JsonFactory f = new JsonFactory(); | |
59 | f.enable(JsonParser.Feature.ALLOW_YAML_COMMENTS); | |
60 | ||
59 | final JsonFactory f = JsonFactory.builder() | |
60 | .enable(JsonReadFeature.ALLOW_YAML_COMMENTS) | |
61 | .build(); | |
61 | 62 | _testYAMLComments(f, 99); |
62 | 63 | _testYAMLComments(f, 3); |
63 | 64 | _testYAMLComments(f, 1); |
72 | 73 | } |
73 | 74 | |
74 | 75 | public void testCCommentsEnabled() throws Exception { |
75 | JsonFactory f = new JsonFactory(); | |
76 | f.enable(JsonParser.Feature.ALLOW_COMMENTS); | |
76 | final JsonFactory f = JsonFactory.builder() | |
77 | .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | |
78 | .build(); | |
77 | 79 | final String COMMENT = "/* foo */\n"; |
78 | 80 | _testCommentsBeforePropValue(f, COMMENT, 99); |
79 | 81 | _testCommentsBeforePropValue(f, COMMENT, 3); |
81 | 83 | } |
82 | 84 | |
83 | 85 | public void testCppCommentsEnabled() throws Exception { |
84 | JsonFactory f = new JsonFactory(); | |
85 | f.enable(JsonParser.Feature.ALLOW_COMMENTS); | |
86 | final JsonFactory f = JsonFactory.builder() | |
87 | .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | |
88 | .build(); | |
86 | 89 | final String COMMENT = "// foo\n"; |
87 | 90 | _testCommentsBeforePropValue(f, COMMENT, 99); |
88 | 91 | _testCommentsBeforePropValue(f, COMMENT, 3); |
240 | 243 | int bytesPerRead) |
241 | 244 | throws IOException |
242 | 245 | { |
243 | JsonFactory f = new JsonFactory(); | |
244 | f.configure(JsonParser.Feature.ALLOW_COMMENTS, enabled); | |
246 | final JsonFactory f = JsonFactory.builder() | |
247 | .configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, enabled) | |
248 | .build(); | |
245 | 249 | return asyncForBytes(f, bytesPerRead, _jsonDoc(doc), 0); |
246 | 250 | } |
247 | 251 |
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | 5 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
6 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
6 | 7 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
7 | 8 | |
8 | 9 | public class AsyncFieldNamesTest extends AsyncTestBase |
9 | 10 | { |
10 | 11 | private final JsonFactory JSON_F = new JsonFactory(); |
11 | 12 | |
12 | private final JsonFactory JSON_APOS_F = new JsonFactory(); | |
13 | { | |
14 | JSON_APOS_F.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); | |
15 | } | |
13 | private final JsonFactory JSON_APOS_F = JsonFactory.builder() | |
14 | .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) | |
15 | .build(); | |
16 | 16 | |
17 | 17 | // Mainly to test "fast" parse for shortish names |
18 | 18 | public void testSimpleFieldNames() throws IOException |
+34
-34
3 | 3 | import java.util.*; |
4 | 4 | |
5 | 5 | import com.fasterxml.jackson.core.*; |
6 | import com.fasterxml.jackson.core.JsonParser.Feature; | |
7 | 6 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
7 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
8 | 8 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
9 | 9 | |
10 | 10 | import org.junit.Test; |
15 | 15 | public class AsyncMissingValuesInArrayTest extends AsyncTestBase |
16 | 16 | { |
17 | 17 | private final JsonFactory factory; |
18 | private final HashSet<JsonParser.Feature> features; | |
19 | ||
20 | public AsyncMissingValuesInArrayTest(Collection<JsonParser.Feature> features) { | |
21 | this.factory = new JsonFactory(); | |
22 | this.features = new HashSet<JsonParser.Feature>(features); | |
23 | ||
24 | for (JsonParser.Feature feature : features) { | |
25 | factory.enable(feature); | |
18 | private final HashSet<JsonReadFeature> features; | |
19 | ||
20 | public AsyncMissingValuesInArrayTest(Collection<JsonReadFeature> features) { | |
21 | this.features = new HashSet<JsonReadFeature>(features); | |
22 | JsonFactoryBuilder b = (JsonFactoryBuilder) JsonFactory.builder(); | |
23 | for (JsonReadFeature feature : features) { | |
24 | b = b.enable(feature); | |
26 | 25 | } |
26 | factory = b.build(); | |
27 | 27 | } |
28 | 28 | |
29 | 29 | @Parameterized.Parameters(name = "Features {0}") |
30 | public static Collection<EnumSet<JsonParser.Feature>> getTestCases() | |
30 | public static Collection<EnumSet<JsonReadFeature>> getTestCases() | |
31 | 31 | { |
32 | List<EnumSet<JsonParser.Feature>> cases = new ArrayList<EnumSet<JsonParser.Feature>>(); | |
33 | cases.add(EnumSet.noneOf(JsonParser.Feature.class)); | |
34 | cases.add(EnumSet.of(Feature.ALLOW_MISSING_VALUES)); | |
35 | cases.add(EnumSet.of(Feature.ALLOW_TRAILING_COMMA)); | |
36 | cases.add(EnumSet.of(Feature.ALLOW_MISSING_VALUES, Feature.ALLOW_TRAILING_COMMA)); | |
32 | List<EnumSet<JsonReadFeature>> cases = new ArrayList<EnumSet<JsonReadFeature>>(); | |
33 | cases.add(EnumSet.noneOf(JsonReadFeature.class)); | |
34 | cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES)); | |
35 | cases.add(EnumSet.of(JsonReadFeature.ALLOW_TRAILING_COMMA)); | |
36 | cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES, JsonReadFeature.ALLOW_TRAILING_COMMA)); | |
37 | 37 | return cases; |
38 | 38 | } |
39 | 39 | |
69 | 69 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
70 | 70 | assertEquals("a", p.currentText()); |
71 | 71 | |
72 | if (!features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
72 | if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
73 | 73 | assertUnexpected(p, ','); |
74 | 74 | return; |
75 | 75 | } |
91 | 91 | |
92 | 92 | assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
93 | 93 | |
94 | if (!features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
94 | if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
95 | 95 | assertUnexpected(p, ','); |
96 | 96 | return; |
97 | 97 | } |
124 | 124 | assertEquals("b", p.currentText()); |
125 | 125 | |
126 | 126 | // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
127 | if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
128 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
129 | assertEnd(p); | |
130 | } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
127 | if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
128 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
129 | assertEnd(p); | |
130 | } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
131 | 131 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
132 | 132 | assertToken(JsonToken.END_ARRAY, p.nextToken()); |
133 | 133 | assertEnd(p); |
152 | 152 | assertEquals("b", p.currentText()); |
153 | 153 | |
154 | 154 | // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
155 | if (features.contains(Feature.ALLOW_MISSING_VALUES) && | |
156 | features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
157 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
158 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
159 | assertEnd(p); | |
160 | } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
155 | if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && | |
156 | features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
157 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
158 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
159 | assertEnd(p); | |
160 | } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
161 | 161 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
162 | 162 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
163 | 163 | assertToken(JsonToken.END_ARRAY, p.nextToken()); |
183 | 183 | assertEquals("b", p.currentText()); |
184 | 184 | |
185 | 185 | // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
186 | if (features.contains(Feature.ALLOW_MISSING_VALUES) && | |
187 | features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
188 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
189 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
190 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
191 | assertEnd(p); | |
192 | } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
186 | if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && | |
187 | features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
188 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
189 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
190 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
191 | assertEnd(p); | |
192 | } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
193 | 193 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
194 | 194 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
195 | 195 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
+16
-19
6 | 6 | import org.junit.runner.RunWith; |
7 | 7 | import org.junit.runners.Parameterized; |
8 | 8 | |
9 | import com.fasterxml.jackson.core.JsonFactory; | |
10 | import com.fasterxml.jackson.core.JsonParseException; | |
11 | import com.fasterxml.jackson.core.JsonParser; | |
12 | import com.fasterxml.jackson.core.JsonToken; | |
13 | import com.fasterxml.jackson.core.JsonParser.Feature; | |
9 | import com.fasterxml.jackson.core.*; | |
14 | 10 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
11 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
15 | 12 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
16 | 13 | |
17 | 14 | @RunWith(Parameterized.class) |
18 | 15 | public class AsyncMissingValuesInObjectTest extends AsyncTestBase |
19 | 16 | { |
20 | 17 | private JsonFactory factory; |
21 | private HashSet<JsonParser.Feature> features; | |
18 | private HashSet<JsonReadFeature> features; | |
22 | 19 | |
23 | public AsyncMissingValuesInObjectTest(Collection<JsonParser.Feature> features) { | |
24 | this.factory = new JsonFactory(); | |
25 | this.features = new HashSet<JsonParser.Feature>(features); | |
26 | ||
27 | for (JsonParser.Feature feature : features) { | |
28 | factory.enable(feature); | |
20 | public AsyncMissingValuesInObjectTest(Collection<JsonReadFeature> features) { | |
21 | this.features = new HashSet<JsonReadFeature>(features); | |
22 | JsonFactoryBuilder b = (JsonFactoryBuilder) JsonFactory.builder(); | |
23 | for (JsonReadFeature feature : features) { | |
24 | b = b.enable(feature); | |
29 | 25 | } |
26 | factory = b.build(); | |
30 | 27 | } |
31 | 28 | |
32 | 29 | @Parameterized.Parameters(name = "Features {0}") |
33 | public static Collection<EnumSet<JsonParser.Feature>> getTestCases() | |
30 | public static Collection<EnumSet<JsonReadFeature>> getTestCases() | |
34 | 31 | { |
35 | List<EnumSet<JsonParser.Feature>> cases = new ArrayList<EnumSet<JsonParser.Feature>>(); | |
36 | cases.add(EnumSet.noneOf(JsonParser.Feature.class)); | |
37 | cases.add(EnumSet.of(Feature.ALLOW_MISSING_VALUES)); | |
38 | cases.add(EnumSet.of(Feature.ALLOW_TRAILING_COMMA)); | |
39 | cases.add(EnumSet.of(Feature.ALLOW_MISSING_VALUES, Feature.ALLOW_TRAILING_COMMA)); | |
32 | List<EnumSet<JsonReadFeature>> cases = new ArrayList<EnumSet<JsonReadFeature>>(); | |
33 | cases.add(EnumSet.noneOf(JsonReadFeature.class)); | |
34 | cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES)); | |
35 | cases.add(EnumSet.of(JsonReadFeature.ALLOW_TRAILING_COMMA)); | |
36 | cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES, JsonReadFeature.ALLOW_TRAILING_COMMA)); | |
40 | 37 | return cases; |
41 | 38 | } |
42 | 39 | |
105 | 102 | assertEquals("b", p.currentText()); |
106 | 103 | assertToken(JsonToken.VALUE_FALSE, p.nextToken()); |
107 | 104 | |
108 | if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
105 | if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
109 | 106 | assertToken(JsonToken.END_OBJECT, p.nextToken()); |
110 | 107 | assertEnd(p); |
111 | 108 | } else { |
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | 5 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
6 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
6 | 7 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
7 | 8 | |
8 | 9 | public class AsyncNonStdNumbersTest extends AsyncTestBase |
9 | 10 | { |
10 | 11 | private final JsonFactory DEFAULT_F = new JsonFactory(); |
11 | 12 | |
13 | @SuppressWarnings("deprecation") | |
14 | public void testDefaultsForAsync() throws Exception { | |
15 | assertFalse(DEFAULT_F.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); | |
16 | } | |
17 | ||
12 | 18 | public void testDisallowNaN() throws Exception |
13 | 19 | { |
14 | 20 | final String JSON = "[ NaN]"; |
15 | assertFalse(DEFAULT_F.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); | |
16 | 21 | |
17 | 22 | // without enabling, should get an exception |
18 | 23 | AsyncReaderWrapper p = createParser(DEFAULT_F, JSON, 1); |
30 | 35 | public void testAllowNaN() throws Exception |
31 | 36 | { |
32 | 37 | final String JSON = "[ NaN]"; |
33 | JsonFactory f = new JsonFactory(); | |
34 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
35 | ||
38 | JsonFactory f = JsonFactory.builder() | |
39 | .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) | |
40 | .build(); | |
36 | 41 | _testAllowNaN(f, JSON, 99); |
37 | 42 | _testAllowNaN(f, JSON, 5); |
38 | 43 | _testAllowNaN(f, JSON, 3); |
61 | 66 | p.close(); |
62 | 67 | |
63 | 68 | // finally, should also work with skipping |
64 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
69 | f = JsonFactory.builder() | |
70 | .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) | |
71 | .build(); | |
65 | 72 | p = createParser(f, doc, readBytes); |
66 | 73 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
67 | 74 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); |
90 | 97 | |
91 | 98 | private void _testDisallowInf(JsonFactory f, String token, int readBytes) throws Exception |
92 | 99 | { |
93 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); | |
94 | 100 | final String JSON = String.format("[%s]", token); |
95 | 101 | |
96 | 102 | // without enabling, should get an exception |
108 | 114 | |
109 | 115 | public void testAllowInf() throws Exception |
110 | 116 | { |
111 | JsonFactory f = new JsonFactory(); | |
112 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
113 | ||
117 | JsonFactory f = JsonFactory.builder() | |
118 | .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) | |
119 | .build(); | |
114 | 120 | String JSON = "[ Infinity, +Infinity, -Infinity ]"; |
115 | 121 | _testAllowInf(f, JSON, 99); |
116 | 122 | _testAllowInf(f, JSON, 5); |
159 | 165 | p.close(); |
160 | 166 | |
161 | 167 | // finally, should also work with skipping |
162 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
168 | f = JsonFactory.builder() | |
169 | .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) | |
170 | .build(); | |
163 | 171 | p = createParser(f, doc, readBytes); |
164 | 172 | |
165 | 173 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | 5 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
6 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
6 | 7 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
7 | 8 | |
8 | 9 | public class AsyncNonStdParsingTest extends AsyncTestBase |
9 | 10 | { |
10 | 11 | public void testLargeUnquotedNames() throws Exception |
11 | 12 | { |
12 | JsonFactory f = new JsonFactory(); | |
13 | f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); | |
14 | ||
13 | final JsonFactory f = JsonFactory.builder() | |
14 | .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) | |
15 | .build(); | |
15 | 16 | StringBuilder sb = new StringBuilder(5000); |
16 | 17 | sb.append("[\n"); |
17 | 18 | final int REPS = 1050; |
58 | 59 | |
59 | 60 | public void testSimpleUnquotedNames() throws Exception |
60 | 61 | { |
61 | final JsonFactory f = new JsonFactory(); | |
62 | f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); | |
63 | ||
62 | final JsonFactory f = JsonFactory.builder() | |
63 | .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) | |
64 | .build(); | |
64 | 65 | _testSimpleUnquoted(f, 0, 99); |
65 | 66 | _testSimpleUnquoted(f, 0, 5); |
66 | 67 | _testSimpleUnquoted(f, 0, 3); |
169 | 170 | */ |
170 | 171 | public void testAposQuotingEnabled() throws Exception |
171 | 172 | { |
172 | JsonFactory f = new JsonFactory(); | |
173 | f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); | |
174 | ||
173 | final JsonFactory f = JsonFactory.builder() | |
174 | .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) | |
175 | .build(); | |
175 | 176 | _testAposQuotingEnabled(f, 0, 99); |
176 | 177 | _testAposQuotingEnabled(f, 0, 5); |
177 | 178 | _testAposQuotingEnabled(f, 0, 3); |
267 | 268 | // test to verify that we implicitly allow escaping of apostrophe |
268 | 269 | public void testSingleQuotesEscaped() throws Exception |
269 | 270 | { |
270 | JsonFactory f = new JsonFactory(); | |
271 | f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); | |
272 | ||
271 | final JsonFactory f = JsonFactory.builder() | |
272 | .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) | |
273 | .build(); | |
273 | 274 | _testSingleQuotesEscaped(f, 0, 99); |
274 | 275 | _testSingleQuotesEscaped(f, 0, 5); |
275 | 276 | _testSingleQuotesEscaped(f, 0, 3); |
294 | 295 | |
295 | 296 | public void testNonStandardNameChars() throws Exception |
296 | 297 | { |
297 | JsonFactory f = new JsonFactory(); | |
298 | f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); | |
299 | ||
298 | final JsonFactory f = JsonFactory.builder() | |
299 | .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) | |
300 | .build(); | |
300 | 301 | _testNonStandardNameChars(f, 0, 99); |
301 | 302 | _testNonStandardNameChars(f, 0, 6); |
302 | 303 | _testNonStandardNameChars(f, 0, 3); |
359 | 360 | { |
360 | 361 | // first: verify that we get an exception |
361 | 362 | JsonFactory f = new JsonFactory(); |
362 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)); | |
363 | 363 | final String JSON = quote("\\'"); |
364 | 364 | AsyncReaderWrapper p = createParser(f, JSON, offset, readSize); |
365 | 365 | try { |
372 | 372 | p.close(); |
373 | 373 | } |
374 | 374 | // and then verify it's ok... |
375 | f.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true); | |
376 | assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)); | |
375 | f = f.rebuild() | |
376 | .enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER) | |
377 | .build(); | |
377 | 378 | p = createParser(f, JSON, offset, readSize); |
378 | 379 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
379 | 380 | assertEquals("'", p.currentText()); |
4 | 4 | import java.math.BigInteger; |
5 | 5 | |
6 | 6 | import com.fasterxml.jackson.core.JsonFactory; |
7 | import com.fasterxml.jackson.core.JsonParseException; | |
8 | 7 | import com.fasterxml.jackson.core.JsonParser.NumberType; |
9 | 8 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
9 | import com.fasterxml.jackson.core.exc.InputCoercionException; | |
10 | 10 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
11 | 11 | import com.fasterxml.jackson.core.JsonToken; |
12 | 12 | |
71 | 71 | try { |
72 | 72 | p.getIntValue(); |
73 | 73 | fail("Should not pass"); |
74 | } catch (JsonParseException e) { | |
75 | verifyException(e, "out of range of int"); | |
74 | } catch (InputCoercionException e) { | |
75 | verifyException(e, "out of range of int"); | |
76 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
77 | assertEquals(Integer.TYPE, e.getTargetType()); | |
76 | 78 | } |
77 | 79 | long small = -1L + Integer.MIN_VALUE; |
78 | 80 | p = createParser(String.valueOf(small)); |
82 | 84 | try { |
83 | 85 | p.getIntValue(); |
84 | 86 | fail("Should not pass"); |
85 | } catch (JsonParseException e) { | |
86 | verifyException(e, "out of range of int"); | |
87 | } catch (InputCoercionException e) { | |
88 | verifyException(e, "out of range of int"); | |
89 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
90 | assertEquals(Integer.TYPE, e.getTargetType()); | |
87 | 91 | } |
88 | 92 | |
89 | 93 | // double -> error |
93 | 97 | try { |
94 | 98 | p.getIntValue(); |
95 | 99 | fail("Should not pass"); |
96 | } catch (JsonParseException e) { | |
97 | verifyException(e, "out of range of int"); | |
100 | } catch (InputCoercionException e) { | |
101 | verifyException(e, "out of range of int"); | |
102 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
103 | assertEquals(Integer.TYPE, e.getTargetType()); | |
98 | 104 | } |
99 | 105 | p = createParser(String.valueOf(small)+".0"); |
100 | 106 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); |
102 | 108 | try { |
103 | 109 | p.getIntValue(); |
104 | 110 | fail("Should not pass"); |
105 | } catch (JsonParseException e) { | |
106 | verifyException(e, "out of range of int"); | |
111 | } catch (InputCoercionException e) { | |
112 | verifyException(e, "out of range of int"); | |
113 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
114 | assertEquals(Integer.TYPE, e.getTargetType()); | |
107 | 115 | } |
108 | 116 | |
109 | 117 | // BigInteger -> error |
113 | 121 | try { |
114 | 122 | p.getIntValue(); |
115 | 123 | fail("Should not pass"); |
116 | } catch (JsonParseException e) { | |
117 | verifyException(e, "out of range of int"); | |
124 | } catch (InputCoercionException e) { | |
125 | verifyException(e, "out of range of int"); | |
126 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
127 | assertEquals(Integer.TYPE, e.getTargetType()); | |
118 | 128 | } |
119 | 129 | p = createParser(String.valueOf(small)); |
120 | 130 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); |
122 | 132 | try { |
123 | 133 | p.getIntValue(); |
124 | 134 | fail("Should not pass"); |
125 | } catch (JsonParseException e) { | |
126 | verifyException(e, "out of range of int"); | |
135 | } catch (InputCoercionException e) { | |
136 | verifyException(e, "out of range of int"); | |
137 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
138 | assertEquals(Integer.TYPE, e.getTargetType()); | |
127 | 139 | } |
128 | 140 | } |
129 | 141 | |
175 | 187 | try { |
176 | 188 | p.getLongValue(); |
177 | 189 | fail("Should not pass"); |
178 | } catch (JsonParseException e) { | |
190 | } catch (InputCoercionException e) { | |
179 | 191 | verifyException(e, "out of range of long"); |
192 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
193 | assertEquals(Long.TYPE, e.getTargetType()); | |
180 | 194 | } |
181 | 195 | BigInteger small = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.TEN); |
182 | 196 | p = createParser(String.valueOf(small)); |
185 | 199 | try { |
186 | 200 | p.getLongValue(); |
187 | 201 | fail("Should not pass"); |
188 | } catch (JsonParseException e) { | |
202 | } catch (InputCoercionException e) { | |
189 | 203 | verifyException(e, "out of range of long"); |
204 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
205 | assertEquals(Long.TYPE, e.getTargetType()); | |
190 | 206 | } |
191 | 207 | } |
192 | 208 |
+14
-6
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | 5 | import com.fasterxml.jackson.core.async.AsyncTestBase; |
6 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
6 | 7 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; |
7 | 8 | |
8 | 9 | public class AsyncNumberLeadingZeroesTest extends AsyncTestBase |
9 | 10 | { |
11 | @SuppressWarnings("deprecation") | |
12 | public void testDefaultsForAsync() throws Exception { | |
13 | JsonFactory f = new JsonFactory(); | |
14 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
15 | } | |
16 | ||
10 | 17 | public void testLeadingZeroesInt() throws Exception |
11 | 18 | { |
12 | 19 | _testLeadingZeroesInt("00003", 3); |
33 | 40 | public void _testLeadingZeroesInt(String valueStr, int value) throws Exception |
34 | 41 | { |
35 | 42 | // first: verify that we get an exception |
43 | ||
36 | 44 | JsonFactory f = new JsonFactory(); |
37 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
38 | 45 | String JSON = valueStr; |
39 | 46 | AsyncReaderWrapper p = createParser(f, JSON); |
40 | 47 | try { |
48 | 55 | } |
49 | 56 | |
50 | 57 | // and then verify it's ok when enabled |
51 | f.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true); | |
52 | assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
58 | f = JsonFactory.builder() | |
59 | .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS) | |
60 | .build(); | |
53 | 61 | p = createParser(f, JSON); |
54 | 62 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); |
55 | 63 | assertEquals(value, p.getIntValue()); |
72 | 80 | { |
73 | 81 | // first: verify that we get an exception |
74 | 82 | JsonFactory f = new JsonFactory(); |
75 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
76 | 83 | String JSON = valueStr; |
77 | 84 | AsyncReaderWrapper p = createParser(f, JSON); |
78 | 85 | try { |
86 | 93 | } |
87 | 94 | |
88 | 95 | // and then verify it's ok when enabled |
89 | f.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true); | |
90 | assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
96 | f = JsonFactory.builder() | |
97 | .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS) | |
98 | .build(); | |
91 | 99 | p = createParser(f, JSON); |
92 | 100 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); |
93 | 101 | assertEquals(String.valueOf(value), p.currentText()); |
+113
-0
0 | package com.fasterxml.jackson.core.json.async; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | import com.fasterxml.jackson.core.async.AsyncTestBase; | |
4 | import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; | |
5 | ||
6 | public class AsyncPointerFromContext563Test extends AsyncTestBase | |
7 | { | |
8 | private final JsonFactory JSON_F = new JsonFactory(); | |
9 | ||
10 | // [core#563] | |
11 | public void testPointerWithAsyncParser() throws Exception | |
12 | { | |
13 | final String SIMPLE = aposToQuotes("{'a':123,'array':[1,2,[3],5,{'obInArray':4}]," | |
14 | +"'ob':{'first':[false,true],'second':{'sub':37}},'b':true}"); | |
15 | byte[] SIMPLE_BYTES = SIMPLE.getBytes("UTF-8"); | |
16 | ||
17 | _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 1000); | |
18 | _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 7); | |
19 | _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 3); | |
20 | _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 2); | |
21 | _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 1); | |
22 | ||
23 | _testPointerWithAsyncParser(SIMPLE_BYTES, 20, 5); | |
24 | _testPointerWithAsyncParser(SIMPLE_BYTES, 14, 1); | |
25 | } | |
26 | ||
27 | public void _testPointerWithAsyncParser(byte[] doc, int offset, int readSize) throws Exception | |
28 | { | |
29 | AsyncReaderWrapper p = asyncForBytes(JSON_F, readSize, doc, offset); | |
30 | ||
31 | // by default should just get "empty" | |
32 | assertSame(JsonPointer.empty(), p.getParsingContext().pathAsPointer()); | |
33 | ||
34 | // let's just traverse, then: | |
35 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
36 | assertSame(JsonPointer.empty(), p.getParsingContext().pathAsPointer()); | |
37 | ||
38 | assertEquals("", p.getParsingContext().pathAsPointer().toString()); | |
39 | ||
40 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // a | |
41 | assertEquals("/a", p.getParsingContext().pathAsPointer().toString()); | |
42 | ||
43 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
44 | assertEquals("/a", p.getParsingContext().pathAsPointer().toString()); | |
45 | ||
46 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // array | |
47 | assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); | |
48 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
49 | assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); | |
50 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 1 | |
51 | assertEquals("/array/0", p.getParsingContext().pathAsPointer().toString()); | |
52 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 2 | |
53 | assertEquals("/array/1", p.getParsingContext().pathAsPointer().toString()); | |
54 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
55 | assertEquals("/array/2", p.getParsingContext().pathAsPointer().toString()); | |
56 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 3 | |
57 | assertEquals("/array/2/0", p.getParsingContext().pathAsPointer().toString()); | |
58 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
59 | assertEquals("/array/2", p.getParsingContext().pathAsPointer().toString()); | |
60 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 5 | |
61 | assertEquals("/array/3", p.getParsingContext().pathAsPointer().toString()); | |
62 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
63 | assertEquals("/array/4", p.getParsingContext().pathAsPointer().toString()); | |
64 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // obInArray | |
65 | assertEquals("/array/4/obInArray", p.getParsingContext().pathAsPointer().toString()); | |
66 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 4 | |
67 | assertEquals("/array/4/obInArray", p.getParsingContext().pathAsPointer().toString()); | |
68 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
69 | assertEquals("/array/4", p.getParsingContext().pathAsPointer().toString()); | |
70 | assertToken(JsonToken.END_ARRAY, p.nextToken()); // /array | |
71 | assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); | |
72 | ||
73 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // ob | |
74 | assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); | |
75 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
76 | assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); | |
77 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // first | |
78 | assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); | |
79 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
80 | assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); | |
81 | assertToken(JsonToken.VALUE_FALSE, p.nextToken()); | |
82 | assertEquals("/ob/first/0", p.getParsingContext().pathAsPointer().toString()); | |
83 | assertToken(JsonToken.VALUE_TRUE, p.nextToken()); | |
84 | assertEquals("/ob/first/1", p.getParsingContext().pathAsPointer().toString()); | |
85 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
86 | assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); | |
87 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // second | |
88 | assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); | |
89 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
90 | assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); | |
91 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // sub | |
92 | assertEquals("/ob/second/sub", p.getParsingContext().pathAsPointer().toString()); | |
93 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 37 | |
94 | assertEquals("/ob/second/sub", p.getParsingContext().pathAsPointer().toString()); | |
95 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
96 | assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); | |
97 | assertToken(JsonToken.END_OBJECT, p.nextToken()); // /ob | |
98 | assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); | |
99 | ||
100 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); // b | |
101 | assertEquals("/b", p.getParsingContext().pathAsPointer().toString()); | |
102 | assertToken(JsonToken.VALUE_TRUE, p.nextToken()); | |
103 | assertEquals("/b", p.getParsingContext().pathAsPointer().toString()); | |
104 | ||
105 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
106 | assertSame(JsonPointer.empty(), p.getParsingContext().pathAsPointer()); | |
107 | ||
108 | // note: wrapper maps to `null`, plain async-parser would give NOT_AVAILABLE | |
109 | assertNull(p.nextToken()); | |
110 | p.close(); | |
111 | } | |
112 | } |
51 | 51 | public void testSkipChildrenFailOnSplit() throws IOException |
52 | 52 | { |
53 | 53 | NonBlockingJsonParser nbParser = (NonBlockingJsonParser) JSON_F.createNonBlockingByteArrayParser(); |
54 | @SuppressWarnings("resource") | |
54 | 55 | FilteringParserDelegate filteredParser = new FilteringParserDelegate(nbParser, |
55 | 56 | TOKEN_FILTER, true, true); |
56 | 57 | nbParser.feedInput(INPUT_BYTES, 0, 5); |
0 | 0 | package com.fasterxml.jackson.core.main; |
1 | 1 | |
2 | 2 | import com.fasterxml.jackson.core.*; |
3 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
3 | 4 | |
4 | 5 | /** |
5 | 6 | * Set of additional unit for verifying array parsing, specifically |
100 | 101 | */ |
101 | 102 | public void testNotMissingValueByEnablingFeature() throws Exception |
102 | 103 | { |
103 | _testNotMissingValueByEnablingFeature(true); | |
104 | _testNotMissingValueByEnablingFeature(false); | |
104 | _testNotMissingValueByEnablingFeature(true); | |
105 | _testNotMissingValueByEnablingFeature(false); | |
105 | 106 | } |
106 | 107 | |
107 | 108 | private void _testMissingValueByEnablingFeature(boolean useStream) throws Exception { |
108 | 109 | String DOC = "[ \"a\",,,,\"abc\", ] "; |
109 | 110 | |
110 | JsonFactory f = new JsonFactory(); | |
111 | f.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true); | |
112 | ||
111 | JsonFactory f = JsonFactory.builder() | |
112 | .enable(JsonReadFeature.ALLOW_MISSING_VALUES) | |
113 | .build(); | |
113 | 114 | JsonParser jp = useStream ? createParserUsingStream(f, DOC, "UTF-8") |
114 | 115 | : createParserUsingReader(f, DOC); |
115 | 116 | |
163 | 164 | } |
164 | 165 | |
165 | 166 | private void _testNotMissingValueByEnablingFeature(boolean useStream) throws Exception { |
166 | final String DOC = "[ \"a\",\"abc\"] "; | |
167 | final String DOC = "[ \"a\",\"abc\"] "; | |
167 | 168 | |
168 | JsonFactory f = new JsonFactory(); | |
169 | f.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true); | |
170 | ||
169 | JsonFactory f = JsonFactory.builder() | |
170 | .enable(JsonReadFeature.ALLOW_MISSING_VALUES) | |
171 | .build(); | |
171 | 172 | JsonParser jp = useStream ? createParserUsingStream(f, DOC, "UTF-8") |
172 | 173 | : createParserUsingReader(f, DOC); |
173 | 174 |
86 | 86 | { |
87 | 87 | JsonFactory f = new JsonFactory(); |
88 | 88 | // let's verify default setting, first: |
89 | assertTrue(f.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)); | |
89 | assertTrue(f.isEnabled(StreamWriteFeature.AUTO_CLOSE_CONTENT)); | |
90 | 90 | StringWriter sw = new StringWriter(); |
91 | 91 | |
92 | 92 | // First, test arrays: |
106 | 106 | public void testNoAutoCloseArraysAndObjects() |
107 | 107 | throws Exception |
108 | 108 | { |
109 | JsonFactory f = new JsonFactory(); | |
110 | f.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); | |
109 | JsonFactory f = JsonFactory.builder() | |
110 | .disable(StreamWriteFeature.AUTO_CLOSE_CONTENT) | |
111 | .build(); | |
111 | 112 | StringWriter sw = new StringWriter(); |
112 | 113 | JsonGenerator jg = f.createGenerator(sw); |
113 | 114 | jg.writeStartArray(); |
127 | 128 | public void testAutoFlushOrNot() throws Exception |
128 | 129 | { |
129 | 130 | JsonFactory f = new JsonFactory(); |
130 | assertTrue(f.isEnabled(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM)); | |
131 | assertTrue(f.isEnabled(StreamWriteFeature.FLUSH_PASSED_TO_STREAM)); | |
131 | 132 | StringWriterForTesting sw = new StringWriterForTesting(); |
132 | 133 | JsonGenerator jg = f.createGenerator(sw); |
133 | 134 | jg.writeStartArray(); |
149 | 150 | jg.close(); |
150 | 151 | |
151 | 152 | // then disable and we should not see flushing again... |
152 | f.disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM); | |
153 | f = JsonFactory.builder() | |
154 | .disable(StreamWriteFeature.FLUSH_PASSED_TO_STREAM) | |
155 | .build(); | |
153 | 156 | // first with a Writer |
154 | 157 | sw = new StringWriterForTesting(); |
155 | 158 | jg = f.createGenerator(sw); |
23 | 23 | { |
24 | 24 | final String DOC = "[ 1 ]"; |
25 | 25 | |
26 | JsonFactory f = new JsonFactory(); | |
27 | ||
28 | 26 | // Check the default settings |
29 | assertTrue(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); | |
27 | assertTrue(sharedStreamFactory().isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); | |
30 | 28 | // then change |
31 | f.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); | |
32 | assertFalse(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); | |
29 | JsonFactory f = JsonFactory.builder() | |
30 | .disable(StreamReadFeature.AUTO_CLOSE_SOURCE) | |
31 | .build(); | |
32 | assertFalse(f.isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); | |
33 | { | |
34 | assertFalse(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); | |
35 | } | |
33 | 36 | @SuppressWarnings("resource") |
34 | 37 | MyReader input = new MyReader(DOC); |
35 | 38 | JsonParser jp = f.createParser(input); |
52 | 55 | { |
53 | 56 | final String DOC = "[ 1 ]"; |
54 | 57 | |
55 | JsonFactory f = new JsonFactory(); | |
56 | f.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); | |
57 | assertTrue(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); | |
58 | JsonFactory f = JsonFactory.builder() | |
59 | .enable(StreamReadFeature.AUTO_CLOSE_SOURCE) | |
60 | .build(); | |
58 | 61 | MyReader input = new MyReader(DOC); |
59 | 62 | JsonParser jp = f.createParser(input); |
60 | 63 | assertFalse(input.isClosed()); |
78 | 81 | public void testNoAutoCloseInputStream() throws Exception |
79 | 82 | { |
80 | 83 | final String DOC = "[ 1 ]"; |
81 | JsonFactory f = new JsonFactory(); | |
82 | ||
83 | f.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); | |
84 | JsonFactory f = JsonFactory.builder() | |
85 | .disable(StreamReadFeature.AUTO_CLOSE_SOURCE) | |
86 | .build(); | |
84 | 87 | MyStream input = new MyStream(DOC.getBytes("UTF-8")); |
85 | 88 | JsonParser jp = f.createParser(input); |
86 | 89 |
2 | 2 | import java.io.*; |
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
5 | 6 | |
6 | 7 | /** |
7 | 8 | * Unit tests for verifying that additional <code>JsonParser.Feature</code> |
13 | 14 | public void testDefaultSettings() throws Exception |
14 | 15 | { |
15 | 16 | JsonFactory f = new JsonFactory(); |
16 | assertTrue(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); | |
17 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); | |
18 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)); | |
19 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); | |
20 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS)); | |
17 | assertTrue(f.isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); | |
21 | 18 | |
22 | 19 | JsonParser p = f.createParser(new StringReader("{}")); |
23 | 20 | _testDefaultSettings(p); |
27 | 24 | p.close(); |
28 | 25 | } |
29 | 26 | |
27 | @SuppressWarnings("deprecation") | |
28 | public void testDeprecatedDefaultSettings() throws Exception | |
29 | { | |
30 | JsonFactory f = sharedStreamFactory(); | |
31 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); | |
32 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS)); | |
33 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)); | |
34 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); | |
35 | } | |
36 | ||
30 | 37 | public void testQuotesRequired() throws Exception |
31 | 38 | { |
32 | 39 | _testQuotesRequired(false); |
100 | 107 | |
101 | 108 | private void _testTabsEnabled(boolean useStream) throws Exception |
102 | 109 | { |
103 | JsonFactory f = new JsonFactory(); | |
104 | f.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); | |
110 | JsonFactory f = JsonFactory.builder() | |
111 | .configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS, true) | |
112 | .build(); | |
105 | 113 | |
106 | 114 | String FIELD = "a\tb"; |
107 | 115 | String VALUE = "\t"; |
141 | 141 | assertEquals("{ }|{ }|[ ]", _generateRoot(jf, new DefaultPrettyPrinter("|"))); |
142 | 142 | } |
143 | 143 | |
144 | // Alternative solution for [Issue#26] | |
144 | // Alternative solution for [jackson-core#26] | |
145 | 145 | public void testCustomRootSeparatorWithFactory() throws Exception |
146 | 146 | { |
147 | JsonFactory jf = new JsonFactory(); | |
148 | jf.setRootValueSeparator("##"); | |
149 | StringWriter sw = new StringWriter(); | |
150 | JsonGenerator gen = jf.createGenerator(sw); | |
147 | JsonFactory f = ((JsonFactoryBuilder)JsonFactory.builder()) | |
148 | .rootValueSeparator("##") | |
149 | .build(); | |
150 | StringWriter sw = new StringWriter(); | |
151 | JsonGenerator gen = f.createGenerator(sw); | |
151 | 152 | gen.writeNumber(13); |
152 | 153 | gen.writeBoolean(false); |
153 | 154 | gen.writeNull(); |
167 | 168 | _writeTestDocument(gen); |
168 | 169 | |
169 | 170 | assertEquals("[3|\"abc\"|[true]|{\"f\"=null;\"f2\"=null}]", sw.toString()); |
171 | gen.close(); | |
170 | 172 | } |
171 | 173 | |
172 | 174 | public void testCustomSeparatorsWithPP() throws Exception |
179 | 181 | .withArrayValueSeparator('|'))); |
180 | 182 | |
181 | 183 | _writeTestDocument(gen); |
184 | gen.close(); | |
182 | 185 | |
183 | 186 | assertEquals("[ 3| \"abc\"| [ true ]| {" + DefaultIndenter.SYS_LF + |
184 | 187 | " \"f\" = null;" + DefaultIndenter.SYS_LF + |
197 | 200 | .withoutSpacesInObjectEntries()); |
198 | 201 | |
199 | 202 | _writeTestDocument(gen); |
203 | gen.close(); | |
200 | 204 | |
201 | 205 | assertEquals("[ 3| \"abc\"| [ true ]| {" + DefaultIndenter.SYS_LF + |
202 | 206 | " \"f\"=null;" + DefaultIndenter.SYS_LF + |
2 | 2 | import java.io.*; |
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
5 | 6 | |
6 | 7 | /** |
7 | 8 | * Unit tests for verifying that support for (non-standard) comments |
71 | 72 | } |
72 | 73 | |
73 | 74 | public void testYAMLCommentsBytes() throws Exception { |
74 | JsonFactory f = new JsonFactory(); | |
75 | f.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true); | |
75 | final JsonFactory f = JsonFactory.builder() | |
76 | .enable(JsonReadFeature.ALLOW_YAML_COMMENTS) | |
77 | .build(); | |
76 | 78 | |
77 | 79 | _testYAMLComments(f, MODE_INPUT_STREAM); |
78 | 80 | _testCommentsBeforePropValue(f, MODE_INPUT_STREAM, "# foo\n"); |
83 | 85 | } |
84 | 86 | |
85 | 87 | public void testYAMLCommentsChars() throws Exception { |
86 | JsonFactory f = new JsonFactory(); | |
87 | f.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true); | |
88 | final JsonFactory f = JsonFactory.builder() | |
89 | .enable(JsonReadFeature.ALLOW_YAML_COMMENTS) | |
90 | .build(); | |
88 | 91 | _testYAMLComments(f, MODE_READER); |
89 | 92 | final String COMMENT = "# foo\n"; |
90 | 93 | _testCommentsBeforePropValue(f, MODE_READER, COMMENT); |
92 | 95 | } |
93 | 96 | |
94 | 97 | public void testCCommentsBytes() throws Exception { |
95 | JsonFactory f = new JsonFactory(); | |
96 | f.configure(JsonParser.Feature.ALLOW_COMMENTS, true); | |
98 | final JsonFactory f = JsonFactory.builder() | |
99 | .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | |
100 | .build(); | |
97 | 101 | final String COMMENT = "/* foo */\n"; |
98 | 102 | _testCommentsBeforePropValue(f, MODE_INPUT_STREAM, COMMENT); |
99 | 103 | _testCommentsBeforePropValue(f, MODE_INPUT_STREAM_THROTTLED, COMMENT); |
101 | 105 | } |
102 | 106 | |
103 | 107 | public void testCCommentsChars() throws Exception { |
104 | JsonFactory f = new JsonFactory(); | |
105 | f.configure(JsonParser.Feature.ALLOW_COMMENTS, true); | |
108 | final JsonFactory f = JsonFactory.builder() | |
109 | .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | |
110 | .build(); | |
106 | 111 | final String COMMENT = "/* foo */\n"; |
107 | 112 | _testCommentsBeforePropValue(f, MODE_READER, COMMENT); |
108 | 113 | } |
109 | 114 | |
110 | 115 | public void testCppCommentsBytes() throws Exception { |
111 | JsonFactory f = new JsonFactory(); | |
112 | f.configure(JsonParser.Feature.ALLOW_COMMENTS, true); | |
116 | final JsonFactory f = JsonFactory.builder() | |
117 | .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | |
118 | .build(); | |
113 | 119 | final String COMMENT = "// foo\n"; |
114 | 120 | _testCommentsBeforePropValue(f, MODE_INPUT_STREAM, COMMENT); |
115 | 121 | _testCommentsBeforePropValue(f, MODE_INPUT_STREAM_THROTTLED, COMMENT); |
117 | 123 | } |
118 | 124 | |
119 | 125 | public void testCppCommentsChars() throws Exception { |
120 | JsonFactory f = new JsonFactory(); | |
121 | f.configure(JsonParser.Feature.ALLOW_COMMENTS, true); | |
126 | final JsonFactory f = JsonFactory.builder() | |
127 | .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) | |
128 | .build(); | |
122 | 129 | final String COMMENT = "// foo \n"; |
123 | 130 | _testCommentsBeforePropValue(f, MODE_READER, COMMENT); |
124 | 131 | } |
276 | 283 | private JsonParser _createParser(String doc, int mode, boolean enabled) |
277 | 284 | throws IOException |
278 | 285 | { |
279 | JsonFactory f = new JsonFactory(); | |
280 | f.configure(JsonParser.Feature.ALLOW_COMMENTS, enabled); | |
286 | final JsonFactory f = JsonFactory.builder() | |
287 | .configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, enabled) | |
288 | .build(); | |
281 | 289 | JsonParser p = createParser(f, mode, doc); |
282 | 290 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
283 | 291 | return p; |
43 | 43 | |
44 | 44 | private void _testIntern(boolean useStream, boolean enableIntern, String expName) throws IOException |
45 | 45 | { |
46 | JsonFactory f = new JsonFactory(); | |
47 | f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, enableIntern); | |
46 | JsonFactory f = JsonFactory.builder() | |
47 | .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, enableIntern) | |
48 | .build(); | |
48 | 49 | assertEquals(enableIntern, f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); |
49 | 50 | final String JSON = "{ \""+expName+"\" : 1}"; |
50 | 51 | JsonParser p = useStream ? |
444 | 445 | |
445 | 446 | JsonParser p = JSON_FACTORY.createParser(input); |
446 | 447 | assertEquals(JsonToken.START_ARRAY, p.nextToken()); |
447 | // should also have skipped first 3 bytes of BOM; but do we have offset available? | |
448 | /* 08-Oct-2013, tatu: Alas, due to [core#111], we have to omit BOM in calculations | |
449 | * as we do not know what the offset is due to -- may need to revisit, if this | |
450 | * discrepancy becomes an issue. For now it just means that BOM is considered | |
451 | * "out of stream" (not part of input). | |
452 | */ | |
448 | ||
453 | 449 | JsonLocation loc = p.getTokenLocation(); |
454 | // so if BOM was consider in-stream (part of input), this should expect 3: | |
455 | assertEquals(0, loc.getByteOffset()); | |
450 | assertEquals(3, loc.getByteOffset()); | |
456 | 451 | assertEquals(-1, loc.getCharOffset()); |
457 | 452 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); |
458 | 453 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); |
0 | package com.fasterxml.jackson.core.read; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | // Tests mostly for [core#229] | |
5 | public class LocationInArrayTest extends com.fasterxml.jackson.core.BaseTest | |
6 | { | |
7 | final JsonFactory JSON_F = new JsonFactory(); | |
8 | ||
9 | // for [core#229] | |
10 | public void testOffsetInArraysBytes() throws Exception { | |
11 | _testOffsetInArrays(true); | |
12 | } | |
13 | ||
14 | // for [core#229] | |
15 | public void testOffsetInArraysChars() throws Exception { | |
16 | _testOffsetInArrays(false); | |
17 | } | |
18 | ||
19 | private void _testOffsetInArrays(boolean useBytes) throws Exception | |
20 | { | |
21 | JsonParser p; | |
22 | final String DOC = " [10, 251,\n 3 ]"; | |
23 | ||
24 | // first, char based: | |
25 | p = useBytes ? JSON_F.createParser(DOC.getBytes("UTF-8")) | |
26 | : JSON_F.createParser(DOC.toCharArray()); | |
27 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
28 | _assertLocation(useBytes, p.getTokenLocation(), 2L, 1, 3); | |
29 | _assertLocation(useBytes, p.getCurrentLocation(), 3L, 1, 4); | |
30 | ||
31 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
32 | _assertLocation(useBytes, p.getTokenLocation(), 3L, 1, 4); | |
33 | assertEquals(10, p.getIntValue()); // just to ensure read proceeds to end | |
34 | // 2-digits so | |
35 | _assertLocation(useBytes, p.getCurrentLocation(), 5L, 1, 6); | |
36 | ||
37 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
38 | _assertLocation(useBytes, p.getTokenLocation(), 7L, 1, 8); | |
39 | assertEquals(251, p.getIntValue()); // just to ensure read proceeds to end | |
40 | _assertLocation(useBytes, p.getCurrentLocation(), 10L, 1, 11); | |
41 | ||
42 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
43 | _assertLocation(useBytes, p.getTokenLocation(), 15L, 2, 4); | |
44 | assertEquals(3, p.getIntValue()); | |
45 | _assertLocation(useBytes, p.getCurrentLocation(), 16L, 2, 5); | |
46 | ||
47 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
48 | _assertLocation(useBytes, p.getTokenLocation(), 18L, 2, 7); | |
49 | _assertLocation(useBytes, p.getCurrentLocation(), 19L, 2, 8); | |
50 | ||
51 | p.close(); | |
52 | } | |
53 | ||
54 | private void _assertLocation(boolean useBytes, JsonLocation loc, long offset, int row, int col) | |
55 | { | |
56 | assertEquals(row, loc.getLineNr()); | |
57 | assertEquals(col, loc.getColumnNr()); | |
58 | ||
59 | if (useBytes) { | |
60 | assertEquals(offset, loc.getByteOffset()); | |
61 | } else { | |
62 | assertEquals(offset, loc.getCharOffset()); | |
63 | } | |
64 | } | |
65 | } |
0 | package com.fasterxml.jackson.core.read; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | // tests for [core#37] | |
5 | public class LocationInObjectTest extends BaseTest | |
6 | { | |
7 | public void testOffsetWithObjectFieldsUsingUTF8() throws Exception | |
8 | { | |
9 | final JsonFactory f = new JsonFactory(); | |
10 | byte[] b = "{\"f1\":\"v1\",\"f2\":{\"f3\":\"v3\"},\"f4\":[true,false],\"f5\":5}".getBytes("UTF-8"); | |
11 | // 1 6 11 16 17 22 28 33 34 39 46 51 | |
12 | JsonParser p = f.createParser(b); | |
13 | ||
14 | assertEquals(JsonToken.START_OBJECT, p.nextToken()); | |
15 | ||
16 | assertEquals(JsonToken.FIELD_NAME, p.nextToken()); | |
17 | assertEquals(1L, p.getTokenLocation().getByteOffset()); | |
18 | assertEquals(JsonToken.VALUE_STRING, p.nextToken()); | |
19 | assertEquals(6L, p.getTokenLocation().getByteOffset()); | |
20 | ||
21 | assertEquals("f2", p.nextFieldName()); | |
22 | assertEquals(11L, p.getTokenLocation().getByteOffset()); | |
23 | assertEquals(JsonToken.START_OBJECT, p.nextValue()); | |
24 | assertEquals(16L, p.getTokenLocation().getByteOffset()); | |
25 | ||
26 | assertEquals("f3", p.nextFieldName()); | |
27 | assertEquals(17L, p.getTokenLocation().getByteOffset()); | |
28 | assertEquals(JsonToken.VALUE_STRING, p.nextValue()); | |
29 | assertEquals(22L, p.getTokenLocation().getByteOffset()); | |
30 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
31 | ||
32 | assertEquals("f4", p.nextFieldName()); | |
33 | assertEquals(28L, p.getTokenLocation().getByteOffset()); | |
34 | assertEquals(JsonToken.START_ARRAY, p.nextValue()); | |
35 | assertEquals(33L, p.getTokenLocation().getByteOffset()); | |
36 | ||
37 | assertEquals(JsonToken.VALUE_TRUE, p.nextValue()); | |
38 | assertEquals(34L, p.getTokenLocation().getByteOffset()); | |
39 | ||
40 | assertEquals(JsonToken.VALUE_FALSE, p.nextValue()); | |
41 | assertEquals(39L, p.getTokenLocation().getByteOffset()); | |
42 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); | |
43 | ||
44 | assertEquals("f5", p.nextFieldName()); | |
45 | assertEquals(46L, p.getTokenLocation().getByteOffset()); | |
46 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
47 | assertEquals(51L, p.getTokenLocation().getByteOffset()); | |
48 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
49 | ||
50 | p.close(); | |
51 | } | |
52 | ||
53 | public void testOffsetWithObjectFieldsUsingReader() throws Exception | |
54 | { | |
55 | final JsonFactory f = new JsonFactory(); | |
56 | char[] c = "{\"f1\":\"v1\",\"f2\":{\"f3\":\"v3\"},\"f4\":[true,false],\"f5\":5}".toCharArray(); | |
57 | // 1 6 11 16 17 22 28 33 34 39 46 51 | |
58 | JsonParser p = f.createParser(c); | |
59 | ||
60 | assertEquals(JsonToken.START_OBJECT, p.nextToken()); | |
61 | ||
62 | assertEquals(JsonToken.FIELD_NAME, p.nextToken()); | |
63 | assertEquals(1L, p.getTokenLocation().getCharOffset()); | |
64 | assertEquals(JsonToken.VALUE_STRING, p.nextToken()); | |
65 | assertEquals(6L, p.getTokenLocation().getCharOffset()); | |
66 | ||
67 | assertEquals("f2", p.nextFieldName()); | |
68 | assertEquals(11L, p.getTokenLocation().getCharOffset()); | |
69 | assertEquals(JsonToken.START_OBJECT, p.nextValue()); | |
70 | assertEquals(16L, p.getTokenLocation().getCharOffset()); | |
71 | ||
72 | assertEquals("f3", p.nextFieldName()); | |
73 | assertEquals(17L, p.getTokenLocation().getCharOffset()); | |
74 | assertEquals(JsonToken.VALUE_STRING, p.nextValue()); | |
75 | assertEquals(22L, p.getTokenLocation().getCharOffset()); | |
76 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
77 | ||
78 | assertEquals("f4", p.nextFieldName()); | |
79 | assertEquals(28L, p.getTokenLocation().getCharOffset()); | |
80 | assertEquals(JsonToken.START_ARRAY, p.nextValue()); | |
81 | assertEquals(33L, p.getTokenLocation().getCharOffset()); | |
82 | ||
83 | assertEquals(JsonToken.VALUE_TRUE, p.nextValue()); | |
84 | assertEquals(34L, p.getTokenLocation().getCharOffset()); | |
85 | ||
86 | assertEquals(JsonToken.VALUE_FALSE, p.nextValue()); | |
87 | assertEquals(39L, p.getTokenLocation().getCharOffset()); | |
88 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); | |
89 | ||
90 | assertEquals("f5", p.nextFieldName()); | |
91 | assertEquals(46L, p.getTokenLocation().getCharOffset()); | |
92 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
93 | assertEquals(51L, p.getTokenLocation().getCharOffset()); | |
94 | assertEquals(JsonToken.END_OBJECT, p.nextToken()); | |
95 | ||
96 | p.close(); | |
97 | } | |
98 | } |
0 | package com.fasterxml.jackson.core.read; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | public class LocationOffsetsTest extends com.fasterxml.jackson.core.BaseTest | |
5 | { | |
6 | final JsonFactory JSON_F = new JsonFactory(); | |
7 | ||
8 | // Trivially simple unit test for basics wrt offsets | |
9 | public void testSimpleInitialOffsets() throws Exception | |
10 | { | |
11 | JsonLocation loc; | |
12 | JsonParser p; | |
13 | final String DOC = "{ }"; | |
14 | ||
15 | // first, char based: | |
16 | p = JSON_F.createParser(DOC); | |
17 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
18 | ||
19 | loc = p.getTokenLocation(); | |
20 | assertEquals(-1L, loc.getByteOffset()); | |
21 | assertEquals(0L, loc.getCharOffset()); | |
22 | assertEquals(1, loc.getLineNr()); | |
23 | assertEquals(1, loc.getColumnNr()); | |
24 | ||
25 | loc = p.getCurrentLocation(); | |
26 | assertEquals(-1L, loc.getByteOffset()); | |
27 | assertEquals(1L, loc.getCharOffset()); | |
28 | assertEquals(1, loc.getLineNr()); | |
29 | assertEquals(2, loc.getColumnNr()); | |
30 | ||
31 | p.close(); | |
32 | ||
33 | // then byte-based | |
34 | ||
35 | p = JSON_F.createParser(DOC.getBytes("UTF-8")); | |
36 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
37 | ||
38 | loc = p.getTokenLocation(); | |
39 | assertEquals(0L, loc.getByteOffset()); | |
40 | assertEquals(-1L, loc.getCharOffset()); | |
41 | assertEquals(1, loc.getLineNr()); | |
42 | assertEquals(1, loc.getColumnNr()); | |
43 | ||
44 | loc = p.getCurrentLocation(); | |
45 | assertEquals(1L, loc.getByteOffset()); | |
46 | assertEquals(-1L, loc.getCharOffset()); | |
47 | assertEquals(1, loc.getLineNr()); | |
48 | assertEquals(2, loc.getColumnNr()); | |
49 | ||
50 | p.close(); | |
51 | } | |
52 | ||
53 | // for [core#111] | |
54 | public void testOffsetWithInputOffset() throws Exception | |
55 | { | |
56 | JsonLocation loc; | |
57 | JsonParser p; | |
58 | // 3 spaces before, 2 after, just for padding | |
59 | byte[] b = " { } ".getBytes("UTF-8"); | |
60 | ||
61 | // and then peel them off | |
62 | p = JSON_F.createParser(b, 3, b.length-5); | |
63 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
64 | ||
65 | loc = p.getTokenLocation(); | |
66 | assertEquals(0L, loc.getByteOffset()); | |
67 | assertEquals(-1L, loc.getCharOffset()); | |
68 | assertEquals(1, loc.getLineNr()); | |
69 | assertEquals(1, loc.getColumnNr()); | |
70 | ||
71 | loc = p.getCurrentLocation(); | |
72 | assertEquals(1L, loc.getByteOffset()); | |
73 | assertEquals(-1L, loc.getCharOffset()); | |
74 | assertEquals(1, loc.getLineNr()); | |
75 | assertEquals(2, loc.getColumnNr()); | |
76 | ||
77 | p.close(); | |
78 | } | |
79 | ||
80 | public void testOffsetWithoutInputOffset() throws Exception | |
81 | { | |
82 | JsonLocation loc; | |
83 | JsonParser p; | |
84 | // 3 spaces before, 2 after, just for padding | |
85 | byte[] b = " { } ".getBytes("UTF-8"); | |
86 | ||
87 | // and then peel them off | |
88 | p = JSON_F.createParser(b); | |
89 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
90 | ||
91 | loc = p.getTokenLocation(); | |
92 | assertEquals(3L, loc.getByteOffset()); | |
93 | assertEquals(-1L, loc.getCharOffset()); | |
94 | assertEquals(1, loc.getLineNr()); | |
95 | assertEquals(4, loc.getColumnNr()); | |
96 | ||
97 | loc = p.getCurrentLocation(); | |
98 | assertEquals(4L, loc.getByteOffset()); | |
99 | assertEquals(-1L, loc.getCharOffset()); | |
100 | assertEquals(1, loc.getLineNr()); | |
101 | assertEquals(5, loc.getColumnNr()); | |
102 | ||
103 | p.close(); | |
104 | } | |
105 | ||
106 | // for [core#533] | |
107 | public void testUtf8Bom() throws Exception | |
108 | { | |
109 | JsonLocation loc; | |
110 | JsonParser p; | |
111 | ||
112 | byte[] b = withUtf8Bom("{ }".getBytes()); | |
113 | ||
114 | // and then peel them off | |
115 | p = JSON_F.createParser(b); | |
116 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
117 | ||
118 | loc = p.getTokenLocation(); | |
119 | assertEquals(3L, loc.getByteOffset()); | |
120 | assertEquals(-1L, loc.getCharOffset()); | |
121 | assertEquals(1, loc.getLineNr()); | |
122 | assertEquals(4, loc.getColumnNr()); | |
123 | ||
124 | loc = p.getCurrentLocation(); | |
125 | assertEquals(4L, loc.getByteOffset()); | |
126 | assertEquals(-1L, loc.getCharOffset()); | |
127 | assertEquals(1, loc.getLineNr()); | |
128 | assertEquals(5, loc.getColumnNr()); | |
129 | ||
130 | p.close(); | |
131 | } | |
132 | ||
133 | public void testUtf8BomWithPadding() throws Exception | |
134 | { | |
135 | JsonLocation loc; | |
136 | JsonParser p; | |
137 | ||
138 | byte[] b = withUtf8Bom(" { }".getBytes()); | |
139 | ||
140 | // and then peel them off | |
141 | p = JSON_F.createParser(b); | |
142 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
143 | ||
144 | loc = p.getTokenLocation(); | |
145 | assertEquals(6L, loc.getByteOffset()); | |
146 | assertEquals(-1L, loc.getCharOffset()); | |
147 | assertEquals(1, loc.getLineNr()); | |
148 | assertEquals(7, loc.getColumnNr()); | |
149 | ||
150 | loc = p.getCurrentLocation(); | |
151 | assertEquals(7L, loc.getByteOffset()); | |
152 | assertEquals(-1L, loc.getCharOffset()); | |
153 | assertEquals(1, loc.getLineNr()); | |
154 | assertEquals(8, loc.getColumnNr()); | |
155 | ||
156 | p.close(); | |
157 | } | |
158 | ||
159 | public void testUtf8BomWithInputOffset() throws Exception | |
160 | { | |
161 | JsonLocation loc; | |
162 | JsonParser p; | |
163 | ||
164 | byte[] b = withUtf8Bom(" { }".getBytes()); | |
165 | ||
166 | // and then peel them off | |
167 | p = JSON_F.createParser(b); | |
168 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
169 | ||
170 | loc = p.getTokenLocation(); | |
171 | assertEquals(6L, loc.getByteOffset()); | |
172 | assertEquals(-1L, loc.getCharOffset()); | |
173 | assertEquals(1, loc.getLineNr()); | |
174 | assertEquals(7, loc.getColumnNr()); | |
175 | ||
176 | loc = p.getCurrentLocation(); | |
177 | assertEquals(7L, loc.getByteOffset()); | |
178 | assertEquals(-1L, loc.getCharOffset()); | |
179 | assertEquals(1, loc.getLineNr()); | |
180 | assertEquals(8, loc.getColumnNr()); | |
181 | ||
182 | p.close(); | |
183 | } | |
184 | ||
185 | private byte[] withUtf8Bom(byte[] bytes) { | |
186 | byte[] arr = new byte[bytes.length + 3]; | |
187 | // write UTF-8 BOM | |
188 | arr[0] = (byte) 0xEF; | |
189 | arr[1] = (byte) 0xBB; | |
190 | arr[2] = (byte) 0xBF; | |
191 | System.arraycopy(bytes, 0, arr, 3, bytes.length); | |
192 | return arr; | |
193 | } | |
194 | } |
0 | package com.fasterxml.jackson.core.read; | |
1 | ||
2 | import com.fasterxml.jackson.core.JsonParser; | |
3 | import com.fasterxml.jackson.core.JsonToken; | |
4 | ||
5 | public class NextNameParserTest | |
6 | extends com.fasterxml.jackson.core.BaseTest | |
7 | { | |
8 | public void testBasicNextNameWithReader() throws Exception | |
9 | { | |
10 | _testBasicNextName(MODE_READER); | |
11 | } | |
12 | ||
13 | public void testBasicNextNameWithStream() throws Exception | |
14 | { | |
15 | _testBasicNextName(MODE_INPUT_STREAM); | |
16 | _testBasicNextName(MODE_INPUT_STREAM_THROTTLED); | |
17 | } | |
18 | ||
19 | private void _testBasicNextName(int mode) throws Exception | |
20 | { | |
21 | final String DOC = aposToQuotes( | |
22 | "{ 'data' : { 'primary' : 15, 'vector' : [ 'yes', false ] },\n" | |
23 | +" 'array' : [ true, {'message':'hello', 'value' : 42, 'misc' : [1, 2] }, null, 0.25 ]\n" | |
24 | +"}"); | |
25 | ||
26 | JsonParser p = createParser(mode, DOC); | |
27 | ||
28 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
29 | ||
30 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); | |
31 | assertEquals("data", p.currentName()); | |
32 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
33 | ||
34 | assertEquals("primary", p.nextFieldName()); | |
35 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
36 | assertEquals(15, p.getIntValue()); | |
37 | ||
38 | assertEquals("vector", p.nextFieldName()); | |
39 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
40 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
41 | assertEquals("yes", p.getText()); | |
42 | assertToken(JsonToken.VALUE_FALSE, p.nextToken()); | |
43 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
44 | ||
45 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
46 | ||
47 | assertEquals("array", p.nextFieldName()); | |
48 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
49 | assertToken(JsonToken.VALUE_TRUE, p.nextToken()); | |
50 | ||
51 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
52 | assertEquals("message", p.nextFieldName()); | |
53 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
54 | assertEquals("hello", p.getText()); | |
55 | assertEquals("value", p.nextFieldName()); | |
56 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
57 | assertEquals(42, p.getIntValue()); | |
58 | assertEquals("misc", p.nextFieldName()); | |
59 | ||
60 | assertToken(JsonToken.START_ARRAY, p.nextToken()); | |
61 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
62 | assertEquals(1, p.getIntValue()); | |
63 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
64 | assertEquals(2, p.getIntValue()); | |
65 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
66 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
67 | ||
68 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
69 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); | |
70 | ||
71 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
72 | ||
73 | assertToken(JsonToken.END_OBJECT, p.nextToken()); | |
74 | ||
75 | p.close(); | |
76 | } | |
77 | } |
0 | 0 | package com.fasterxml.jackson.core.read; |
1 | 1 | |
2 | 2 | import com.fasterxml.jackson.core.*; |
3 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
3 | 4 | |
4 | 5 | public class NonStandardParserFeaturesTest |
5 | 6 | extends com.fasterxml.jackson.core.BaseTest |
6 | 7 | { |
8 | @SuppressWarnings("deprecation") | |
9 | public void testDefaults() { | |
10 | JsonFactory f = new JsonFactory(); | |
11 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
12 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); | |
13 | } | |
14 | ||
7 | 15 | public void testSingleQuotesDefault() throws Exception |
8 | 16 | { |
9 | 17 | _testSingleQuotesDefault(MODE_INPUT_STREAM); |
79 | 87 | /**************************************************************** |
80 | 88 | */ |
81 | 89 | |
82 | ||
83 | 90 | /** |
84 | 91 | * Test to verify that the default parser settings do not |
85 | 92 | * accept single-quotes for String values (field names, |
121 | 128 | */ |
122 | 129 | private void _testSingleQuotesEnabled(int mode) throws Exception |
123 | 130 | { |
124 | JsonFactory f = new JsonFactory(); | |
125 | f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); | |
126 | ||
131 | JsonFactory f = JsonFactory.builder() | |
132 | .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) | |
133 | .build(); | |
127 | 134 | String JSON = "{ 'a' : 1, \"foobar\": 'b', '_abcde1234':'d', '\"' : '\"\"', '':'' }"; |
128 | 135 | JsonParser p = createParser(f, mode, JSON); |
129 | 136 | |
198 | 205 | // test to verify that we implicitly allow escaping of apostrophe |
199 | 206 | private void _testSingleQuotesEscaped(int mode) throws Exception |
200 | 207 | { |
201 | JsonFactory f = new JsonFactory(); | |
202 | f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); | |
203 | ||
208 | JsonFactory f = JsonFactory.builder() | |
209 | .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) | |
210 | .build(); | |
204 | 211 | String JSON = "[ '16\\'' ]"; |
205 | 212 | JsonParser p = createParser(f, mode, JSON); |
206 | 213 | |
213 | 220 | |
214 | 221 | private void _testNonStandardNameChars(int mode) throws Exception |
215 | 222 | { |
216 | JsonFactory f = new JsonFactory(); | |
217 | f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); | |
223 | final JsonFactory f = JsonFactory.builder() | |
224 | .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) | |
225 | .build(); | |
218 | 226 | String JSON = "{ @type : \"mytype\", #color : 123, *error* : true, " |
219 | 227 | +" hyphen-ated : \"yes\", me+my : null" |
220 | 228 | +"}"; |
253 | 261 | { |
254 | 262 | // first: verify that we get an exception |
255 | 263 | JsonFactory f = new JsonFactory(); |
256 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)); | |
257 | 264 | final String JSON = quote("\\'"); |
258 | 265 | JsonParser p = createParser(f, mode, JSON); |
259 | 266 | try { |
266 | 273 | p.close(); |
267 | 274 | } |
268 | 275 | // and then verify it's ok... |
269 | f.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true); | |
270 | assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)); | |
276 | f = f.rebuild() | |
277 | .configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true) | |
278 | .build(); | |
271 | 279 | p = createParser(f, mode, JSON); |
272 | 280 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
273 | 281 | assertEquals("'", p.getText()); |
278 | 286 | { |
279 | 287 | // first: verify that we get an exception |
280 | 288 | JsonFactory f = new JsonFactory(); |
281 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
282 | 289 | String JSON = "00003"; |
283 | 290 | if (appendSpace) { |
284 | 291 | JSON += " "; |
293 | 300 | } finally { |
294 | 301 | p.close(); |
295 | 302 | } |
296 | ||
303 | ||
297 | 304 | // and then verify it's ok when enabled |
298 | f.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true); | |
299 | assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); | |
305 | f = JsonFactory.builder() | |
306 | .configure(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS, true) | |
307 | .build(); | |
300 | 308 | p = createParser(f, mode, JSON); |
301 | 309 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); |
302 | 310 | assertEquals(3, p.getIntValue()); |
321 | 329 | { |
322 | 330 | final String JSON = "[ NaN]"; |
323 | 331 | JsonFactory f = new JsonFactory(); |
324 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); | |
325 | 332 | |
326 | 333 | // without enabling, should get an exception |
327 | 334 | JsonParser p = createParser(f, mode, JSON); |
336 | 343 | } |
337 | 344 | |
338 | 345 | // we can enable it dynamically (impl detail) |
339 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
346 | f = JsonFactory.builder() | |
347 | .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) | |
348 | .build(); | |
340 | 349 | p = createParser(f, mode, JSON); |
341 | 350 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
342 | 351 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); |
357 | 366 | p.close(); |
358 | 367 | |
359 | 368 | // finally, should also work with skipping |
360 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
369 | f = JsonFactory.builder() | |
370 | .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) | |
371 | .build(); | |
361 | 372 | p = createParser(f, mode, JSON); |
362 | 373 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
363 | 374 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); |
369 | 380 | { |
370 | 381 | final String JSON = "[ -INF, +INF, +Infinity, Infinity, -Infinity ]"; |
371 | 382 | JsonFactory f = new JsonFactory(); |
372 | assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); | |
373 | 383 | |
374 | 384 | // without enabling, should get an exception |
375 | 385 | JsonParser p = createParser(f, mode, JSON); |
382 | 392 | } finally { |
383 | 393 | p.close(); |
384 | 394 | } |
385 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
395 | f = JsonFactory.builder() | |
396 | .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) | |
397 | .build(); | |
386 | 398 | p = createParser(f, mode, JSON); |
387 | 399 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
388 | 400 | |
420 | 432 | p.close(); |
421 | 433 | |
422 | 434 | // finally, should also work with skipping |
423 | f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); | |
435 | f = JsonFactory.builder() | |
436 | .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) | |
437 | .build(); | |
424 | 438 | p = createParser(f, mode, JSON); |
425 | 439 | |
426 | 440 | assertToken(JsonToken.START_ARRAY, p.nextToken()); |
2 | 2 | import java.math.BigDecimal; |
3 | 3 | import java.math.BigInteger; |
4 | 4 | |
5 | import com.fasterxml.jackson.core.BaseTest; | |
6 | import com.fasterxml.jackson.core.JsonParseException; | |
7 | import com.fasterxml.jackson.core.JsonParser; | |
5 | import com.fasterxml.jackson.core.*; | |
8 | 6 | import com.fasterxml.jackson.core.JsonParser.NumberType; |
9 | import com.fasterxml.jackson.core.JsonToken; | |
7 | import com.fasterxml.jackson.core.exc.InputCoercionException; | |
10 | 8 | |
11 | 9 | public class NumberCoercionTest extends BaseTest |
12 | 10 | { |
65 | 63 | try { |
66 | 64 | p.getIntValue(); |
67 | 65 | fail("Should not pass"); |
68 | } catch (JsonParseException e) { | |
69 | verifyException(e, "out of range of int"); | |
66 | } catch (InputCoercionException e) { | |
67 | verifyException(e, "out of range of int"); | |
68 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
69 | assertEquals(Integer.TYPE, e.getTargetType()); | |
70 | 70 | } |
71 | 71 | long small = -1L + Integer.MIN_VALUE; |
72 | 72 | p = createParser(mode, String.valueOf(small)); |
76 | 76 | try { |
77 | 77 | p.getIntValue(); |
78 | 78 | fail("Should not pass"); |
79 | } catch (JsonParseException e) { | |
80 | verifyException(e, "out of range of int"); | |
79 | } catch (InputCoercionException e) { | |
80 | verifyException(e, "out of range of int"); | |
81 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
82 | assertEquals(Integer.TYPE, e.getTargetType()); | |
81 | 83 | } |
82 | 84 | |
83 | 85 | // double -> error |
87 | 89 | try { |
88 | 90 | p.getIntValue(); |
89 | 91 | fail("Should not pass"); |
90 | } catch (JsonParseException e) { | |
91 | verifyException(e, "out of range of int"); | |
92 | } catch (InputCoercionException e) { | |
93 | verifyException(e, "out of range of int"); | |
94 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
95 | assertEquals(Integer.TYPE, e.getTargetType()); | |
92 | 96 | } |
93 | 97 | p = createParser(mode, String.valueOf(small)+".0"); |
94 | 98 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); |
96 | 100 | try { |
97 | 101 | p.getIntValue(); |
98 | 102 | fail("Should not pass"); |
99 | } catch (JsonParseException e) { | |
100 | verifyException(e, "out of range of int"); | |
103 | } catch (InputCoercionException e) { | |
104 | verifyException(e, "out of range of int"); | |
105 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
106 | assertEquals(Integer.TYPE, e.getTargetType()); | |
101 | 107 | } |
102 | 108 | |
103 | 109 | // BigInteger -> error |
107 | 113 | try { |
108 | 114 | p.getIntValue(); |
109 | 115 | fail("Should not pass"); |
110 | } catch (JsonParseException e) { | |
111 | verifyException(e, "out of range of int"); | |
116 | } catch (InputCoercionException e) { | |
117 | verifyException(e, "out of range of int"); | |
118 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
119 | assertEquals(Integer.TYPE, e.getTargetType()); | |
112 | 120 | } |
113 | 121 | p = createParser(mode, String.valueOf(small)); |
114 | 122 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); |
116 | 124 | try { |
117 | 125 | p.getIntValue(); |
118 | 126 | fail("Should not pass"); |
119 | } catch (JsonParseException e) { | |
120 | verifyException(e, "out of range of int"); | |
127 | } catch (InputCoercionException e) { | |
128 | verifyException(e, "out of range of int"); | |
129 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
130 | assertEquals(Integer.TYPE, e.getTargetType()); | |
121 | 131 | } |
122 | 132 | } |
123 | 133 | } |
174 | 184 | try { |
175 | 185 | p.getLongValue(); |
176 | 186 | fail("Should not pass"); |
177 | } catch (JsonParseException e) { | |
187 | } catch (InputCoercionException e) { | |
178 | 188 | verifyException(e, "out of range of long"); |
189 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
190 | assertEquals(Long.TYPE, e.getTargetType()); | |
179 | 191 | } |
180 | 192 | BigInteger small = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.TEN); |
181 | 193 | p = createParser(mode, String.valueOf(small)); |
184 | 196 | try { |
185 | 197 | p.getLongValue(); |
186 | 198 | fail("Should not pass"); |
187 | } catch (JsonParseException e) { | |
199 | } catch (InputCoercionException e) { | |
188 | 200 | verifyException(e, "out of range of long"); |
201 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
202 | assertEquals(Long.TYPE, e.getTargetType()); | |
189 | 203 | } |
190 | 204 | } |
191 | 205 | } |
2 | 2 | import java.math.BigInteger; |
3 | 3 | |
4 | 4 | import com.fasterxml.jackson.core.*; |
5 | import com.fasterxml.jackson.core.exc.InputCoercionException; | |
5 | 6 | |
6 | 7 | public class NumberOverflowTest |
7 | 8 | extends com.fasterxml.jackson.core.BaseTest |
38 | 39 | try { |
39 | 40 | long x = p.getLongValue(); |
40 | 41 | fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x); |
41 | } catch (JsonParseException e) { | |
42 | } catch (InputCoercionException e) { | |
42 | 43 | verifyException(e, "out of range of long"); |
43 | 44 | } |
44 | 45 | p.close(); |
48 | 49 | try { |
49 | 50 | long x = p.getLongValue(); |
50 | 51 | fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x); |
51 | } catch (JsonParseException e) { | |
52 | } catch (InputCoercionException e) { | |
52 | 53 | verifyException(e, "out of range of long"); |
53 | 54 | } |
54 | 55 | p.close(); |
69 | 70 | try { |
70 | 71 | p.getLongValue(); |
71 | 72 | fail("Should not pass"); |
72 | } catch (JsonParseException e) { | |
73 | } catch (InputCoercionException e) { | |
73 | 74 | verifyException(e, "out of range of long"); |
74 | 75 | verifyException(e, "Integer with "+BIG_NUM_LEN+" digits"); |
75 | 76 | } |
89 | 90 | try { |
90 | 91 | p.getIntValue(); |
91 | 92 | fail("Should not pass"); |
92 | } catch (JsonParseException e) { | |
93 | } catch (InputCoercionException e) { | |
93 | 94 | verifyException(e, "out of range of int"); |
94 | 95 | verifyException(e, "Integer with "+BIG_NUM_LEN+" digits"); |
95 | 96 | } |
6 | 6 | import java.math.BigInteger; |
7 | 7 | |
8 | 8 | import com.fasterxml.jackson.core.*; |
9 | import com.fasterxml.jackson.core.exc.InputCoercionException; | |
10 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
9 | 11 | |
10 | 12 | /** |
11 | 13 | * Set of basic unit tests for verifying that the basic parser |
123 | 125 | // Should get an exception if trying to convert to int |
124 | 126 | try { |
125 | 127 | p.getIntValue(); |
126 | } catch (JsonParseException pe) { | |
127 | verifyException(pe, "out of range"); | |
128 | } catch (InputCoercionException e) { | |
129 | verifyException(e, "out of range"); | |
130 | assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); | |
131 | assertEquals(Integer.TYPE, e.getTargetType()); | |
128 | 132 | } |
129 | 133 | assertEquals((double) EXP_L, p.getDoubleValue()); |
130 | 134 | assertEquals(BigDecimal.valueOf((long) EXP_L), p.getDecimalValue()); |
427 | 431 | */ |
428 | 432 | public void testParsingOfLongerSequencesWithNonNumeric() throws Exception |
429 | 433 | { |
430 | JsonFactory f = new JsonFactory(); | |
431 | f.enable(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS); | |
434 | JsonFactory f = JsonFactory.builder() | |
435 | .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) | |
436 | .build(); | |
432 | 437 | _testParsingOfLongerSequencesWithNonNumeric(f, MODE_INPUT_STREAM); |
433 | 438 | _testParsingOfLongerSequencesWithNonNumeric(f, MODE_INPUT_STREAM_THROTTLED); |
434 | 439 | _testParsingOfLongerSequencesWithNonNumeric(f, MODE_READER); |
3 | 3 | import java.util.*; |
4 | 4 | |
5 | 5 | import com.fasterxml.jackson.core.*; |
6 | import com.fasterxml.jackson.core.JsonParser.Feature; | |
7 | 6 | import com.fasterxml.jackson.core.io.SerializedString; |
7 | import com.fasterxml.jackson.core.json.JsonReadFeature; | |
8 | 8 | import com.fasterxml.jackson.core.json.UTF8DataInputJsonParser; |
9 | 9 | |
10 | 10 | import org.junit.Test; |
12 | 12 | import org.junit.runners.Parameterized; |
13 | 13 | |
14 | 14 | @RunWith(Parameterized.class) |
15 | @SuppressWarnings("resource") | |
15 | 16 | public class TrailingCommasTest extends BaseTest { |
16 | 17 | |
17 | 18 | private final JsonFactory factory; |
18 | private final Set<JsonParser.Feature> features; | |
19 | private final Set<JsonReadFeature> features; | |
19 | 20 | private final int mode; |
20 | 21 | |
21 | public TrailingCommasTest(int mode, List<Feature> features) { | |
22 | this.factory = new JsonFactory(); | |
23 | this.features = new HashSet<JsonParser.Feature>(features); | |
24 | ||
25 | for (JsonParser.Feature feature : features) { | |
26 | factory.enable(feature); | |
27 | } | |
28 | ||
29 | this.mode = mode; | |
22 | public TrailingCommasTest(int mode, List<JsonReadFeature> features) { | |
23 | this.features = new HashSet<JsonReadFeature>(features); | |
24 | JsonFactoryBuilder b = (JsonFactoryBuilder) JsonFactory.builder(); | |
25 | for (JsonReadFeature feature : features) { | |
26 | b = b.enable(feature); | |
27 | } | |
28 | factory = b.build(); | |
29 | this.mode = mode; | |
30 | 30 | } |
31 | 31 | |
32 | 32 | @Parameterized.Parameters(name = "Mode {0}, Features {1}") |
35 | 35 | |
36 | 36 | for (int mode : ALL_MODES) { |
37 | 37 | cases.add(new Object[]{mode, Collections.emptyList()}); |
38 | cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES)}); | |
39 | cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_TRAILING_COMMA)}); | |
40 | cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES, Feature.ALLOW_TRAILING_COMMA)}); | |
38 | cases.add(new Object[]{mode, Arrays.asList(JsonReadFeature.ALLOW_MISSING_VALUES)}); | |
39 | cases.add(new Object[]{mode, Arrays.asList(JsonReadFeature.ALLOW_TRAILING_COMMA)}); | |
40 | cases.add(new Object[]{mode, Arrays.asList(JsonReadFeature.ALLOW_MISSING_VALUES, | |
41 | JsonReadFeature.ALLOW_TRAILING_COMMA)}); | |
41 | 42 | } |
42 | 43 | |
43 | 44 | return cases; |
44 | 45 | } |
45 | 46 | |
46 | @SuppressWarnings("resource") | |
47 | 47 | @Test |
48 | 48 | public void testArrayBasic() throws Exception { |
49 | 49 | String json = "[\"a\", \"b\"]"; |
62 | 62 | assertEnd(p); |
63 | 63 | } |
64 | 64 | |
65 | @SuppressWarnings("resource") | |
66 | 65 | @Test |
67 | 66 | public void testArrayInnerComma() throws Exception { |
68 | 67 | String json = "[\"a\",, \"b\"]"; |
74 | 73 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
75 | 74 | assertEquals("a", p.getText()); |
76 | 75 | |
77 | if (!features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
76 | if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
77 | assertUnexpected(p, ','); | |
78 | return; | |
79 | } | |
80 | ||
81 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
82 | ||
83 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
84 | assertEquals("b", p.getText()); | |
85 | ||
86 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); | |
87 | assertEnd(p); | |
88 | } | |
89 | ||
90 | @Test | |
91 | public void testArrayLeadingComma() throws Exception { | |
92 | String json = "[,\"a\", \"b\"]"; | |
93 | ||
94 | JsonParser p = createParser(factory, mode, json); | |
95 | ||
96 | assertEquals(JsonToken.START_ARRAY, p.nextToken()); | |
97 | ||
98 | if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
78 | 99 | assertUnexpected(p, ','); |
79 | 100 | return; |
80 | 101 | } |
82 | 103 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
83 | 104 | |
84 | 105 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); |
106 | assertEquals("a", p.getText()); | |
107 | ||
108 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
85 | 109 | assertEquals("b", p.getText()); |
86 | 110 | |
87 | 111 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); |
88 | 112 | assertEnd(p); |
89 | } | |
90 | ||
91 | @SuppressWarnings("resource") | |
92 | @Test | |
93 | public void testArrayLeadingComma() throws Exception { | |
94 | String json = "[,\"a\", \"b\"]"; | |
95 | ||
96 | JsonParser p = createParser(factory, mode, json); | |
97 | ||
98 | assertEquals(JsonToken.START_ARRAY, p.nextToken()); | |
99 | ||
100 | if (!features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
101 | assertUnexpected(p, ','); | |
102 | return; | |
103 | } | |
104 | ||
105 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
106 | ||
107 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
108 | assertEquals("a", p.getText()); | |
109 | ||
110 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); | |
111 | assertEquals("b", p.getText()); | |
112 | ||
113 | assertEquals(JsonToken.END_ARRAY, p.nextToken()); | |
114 | assertEnd(p); | |
115 | 113 | p.close(); |
116 | 114 | } |
117 | 115 | |
130 | 128 | assertEquals("b", p.getText()); |
131 | 129 | |
132 | 130 | // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
133 | if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
134 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
135 | assertEnd(p); | |
136 | } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
131 | if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
132 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
133 | assertEnd(p); | |
134 | } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
137 | 135 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
138 | 136 | assertToken(JsonToken.END_ARRAY, p.nextToken()); |
139 | 137 | assertEnd(p); |
158 | 156 | assertEquals("b", p.getText()); |
159 | 157 | |
160 | 158 | // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
161 | if (features.contains(Feature.ALLOW_MISSING_VALUES) && | |
162 | features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
163 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
164 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
165 | assertEnd(p); | |
166 | } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
159 | if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && | |
160 | features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
161 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
162 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
163 | assertEnd(p); | |
164 | } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
167 | 165 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
168 | 166 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
169 | 167 | assertToken(JsonToken.END_ARRAY, p.nextToken()); |
189 | 187 | assertEquals("b", p.getText()); |
190 | 188 | |
191 | 189 | // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES |
192 | if (features.contains(Feature.ALLOW_MISSING_VALUES) && | |
193 | features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
194 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
195 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
196 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
197 | assertEnd(p); | |
198 | } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) { | |
190 | if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && | |
191 | features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
192 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
193 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); | |
194 | assertToken(JsonToken.END_ARRAY, p.nextToken()); | |
195 | assertEnd(p); | |
196 | } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { | |
199 | 197 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
200 | 198 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
201 | 199 | assertToken(JsonToken.VALUE_NULL, p.nextToken()); |
272 | 270 | assertEquals("b", p.getText()); |
273 | 271 | assertToken(JsonToken.VALUE_FALSE, p.nextToken()); |
274 | 272 | |
275 | if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
273 | if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
276 | 274 | assertToken(JsonToken.END_OBJECT, p.nextToken()); |
277 | 275 | assertEnd(p); |
278 | 276 | } else { |
294 | 292 | assertEquals("b", p.nextFieldName()); |
295 | 293 | assertToken(JsonToken.VALUE_FALSE, p.nextToken()); |
296 | 294 | |
297 | if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
295 | if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
298 | 296 | assertEquals(null, p.nextFieldName()); |
299 | 297 | assertToken(JsonToken.END_OBJECT, p.currentToken()); |
300 | 298 | assertEnd(p); |
323 | 321 | assertTrue(p.nextFieldName(new SerializedString("b"))); |
324 | 322 | assertToken(JsonToken.VALUE_FALSE, p.nextToken()); |
325 | 323 | |
326 | if (features.contains(Feature.ALLOW_TRAILING_COMMA)) { | |
324 | if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { | |
327 | 325 | assertFalse(p.nextFieldName(new SerializedString("c"))); |
328 | 326 | assertToken(JsonToken.END_OBJECT, p.currentToken()); |
329 | 327 | assertEnd(p); |
1 | 1 | |
2 | 2 | import java.io.*; |
3 | 3 | import java.lang.reflect.Field; |
4 | import java.util.Random; | |
4 | 5 | |
5 | 6 | import com.fasterxml.jackson.core.*; |
6 | 7 | |
119 | 120 | p.close(); |
120 | 121 | } |
121 | 122 | |
123 | // [core#548] | |
124 | public void testQuadsIssue548() | |
125 | { | |
126 | Random r = new Random(42); | |
127 | ByteQuadsCanonicalizer root = ByteQuadsCanonicalizer.createRoot(); | |
128 | ByteQuadsCanonicalizer canon = root.makeChild(JsonFactory.Feature.collectDefaults()); | |
129 | ||
130 | int n_collisions = 25; | |
131 | int[] collisions = new int[n_collisions]; | |
132 | ||
133 | // generate collisions | |
134 | { | |
135 | int maybe = r.nextInt(); | |
136 | int hash = canon.calcHash(maybe); | |
137 | int target = ((hash & (2048-1)) << 2); | |
138 | ||
139 | for (int i = 0; i < collisions.length; ) { | |
140 | maybe = r.nextInt(); | |
141 | hash = canon.calcHash(maybe); | |
142 | int offset = ((hash & (2048-1)) << 2); | |
143 | ||
144 | if (offset == target) { | |
145 | collisions[i++] = maybe; | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
150 | // fill spillover area until _needRehash is true. | |
151 | for(int i = 0; i < 22 ; i++) { | |
152 | canon.addName(Integer.toString(i), collisions[i]); | |
153 | } | |
154 | // canon._needRehash is now true, since the spillover is full | |
155 | ||
156 | // release table to update tableinfo with canon's data | |
157 | canon.release(); | |
158 | ||
159 | // new table pulls data from new tableinfo, that has a full spillover, but set _needRehash to false | |
160 | canon = root.makeChild(JsonFactory.Feature.collectDefaults()); | |
161 | ||
162 | // canon._needRehash == false, so this will try to add another item to the spillover area, even though it is full | |
163 | canon.addName(Integer.toString(22), collisions[22]); | |
164 | } | |
165 | ||
122 | 166 | /* |
123 | 167 | /********************************************************** |
124 | 168 | /* Helper methods |
32 | 32 | "@~~", "A_~", "A`_", "Aa@", "Ab!", "B@~", // "BA_", "BB@", "BC!", "C!~" |
33 | 33 | }; |
34 | 34 | */ |
35 | ||
35 | ||
36 | 36 | public void testReaderCollisions() throws Exception |
37 | 37 | { |
38 | 38 | StringBuilder sb = new StringBuilder(); |
52 | 52 | |
53 | 53 | // First: attempt with exceptions turned on; should catch an exception |
54 | 54 | |
55 | JsonFactory jf = new JsonFactory(); | |
56 | JsonParser jp = jf.createParser(sb.toString()); | |
57 | jf.enable(JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW); | |
55 | JsonFactory f = JsonFactory.builder() | |
56 | .enable(JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW) | |
57 | .build(); | |
58 | JsonParser p = f.createParser(sb.toString()); | |
58 | 59 | |
59 | 60 | try { |
60 | while (jp.nextToken() != null) { | |
61 | while (p.nextToken() != null) { | |
61 | 62 | ; |
62 | 63 | } |
63 | 64 | fail("Should have failed"); |
64 | 65 | } catch (IllegalStateException e) { |
65 | 66 | verifyException(e, "hash collision"); |
66 | 67 | } |
67 | jp.close(); | |
68 | p.close(); | |
68 | 69 | |
69 | 70 | // but then without feature, should pass |
70 | jf = new JsonFactory(); | |
71 | jf.disable(JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW); | |
72 | jp = jf.createParser(sb.toString()); | |
73 | while (jp.nextToken() != null) { | |
71 | f = JsonFactory.builder() | |
72 | .disable(JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW) | |
73 | .build(); | |
74 | p = f.createParser(sb.toString()); | |
75 | while (p.nextToken() != null) { | |
74 | 76 | ; |
75 | 77 | } |
76 | jp.close(); | |
78 | p.close(); | |
77 | 79 | } |
78 | 80 | |
79 | 81 | /* |
110 | 112 | * Helper class to use for generating substrings to use for substring |
111 | 113 | * substitution collisions. |
112 | 114 | */ |
113 | public static class CollisionGenerator | |
115 | static class CollisionGenerator | |
114 | 116 | { |
115 | 117 | /* JDK uses 31, but Jackson `CharsToNameCanonicalizer.HASH_MULT`, |
116 | 118 | * which for 2.3 is 33. |
21 | 21 | for (int i = 0; i < COUNT; ++i) { |
22 | 22 | String id = fieldNameFor(i); |
23 | 23 | char[] ch = id.toCharArray(); |
24 | ||
24 | 25 | symbols.findSymbol(ch, 0, ch.length, symbols.calcHash(id)); |
25 | 26 | } |
26 | 27 | |
35 | 36 | assertEquals(3431, symbols.collisionCount()); |
36 | 37 | |
37 | 38 | assertEquals(6, symbols.maxCollisionLength()); |
39 | ||
40 | // and final validation | |
41 | symbols.verifyInternalConsistency(); | |
38 | 42 | } |
39 | 43 | |
40 | 44 | public void testSyntheticWithBytesNew() throws IOException |
82 | 86 | symbolsC.calcHash(name)); |
83 | 87 | assertNotNull(str); |
84 | 88 | } |
89 | // validate further, just to make sure | |
90 | symbolsC.verifyInternalConsistency(); | |
91 | ||
85 | 92 | symbolsC.release(); |
86 | 93 | exp += 250; |
87 | 94 | if (exp > CharsToNameCanonicalizer.MAX_ENTRIES_FOR_REUSE) { |
89 | 96 | } |
90 | 97 | assertEquals(exp, symbolsCRoot.size()); |
91 | 98 | } |
99 | ||
100 | // Note: can not validate root instance, is not set up same way | |
92 | 101 | } |
93 | 102 | |
94 | 103 | // Since 2.6 |
121 | 130 | } |
122 | 131 | assertEquals(exp, symbolsBRoot.size()); |
123 | 132 | } |
124 | /* 05-Feb-2015, tatu: Fragile, but it is important to ensure that collision | |
125 | * rates are not accidentally increased... | |
126 | */ | |
133 | ||
134 | // 05-Feb-2015, tatu: Fragile, but it is important to ensure that collision | |
135 | // rates are not accidentally increased... | |
127 | 136 | assertEquals(6250, symbolsB.size()); |
128 | 137 | assertEquals(4761, symbolsB.primaryCount()); // 80% primary hit rate |
129 | 138 | assertEquals(1190, symbolsB.secondaryCount()); // 13% secondary |
6 | 6 | |
7 | 7 | import com.fasterxml.jackson.core.JsonParser; |
8 | 8 | import com.fasterxml.jackson.core.JsonParser.NumberType; |
9 | import com.fasterxml.jackson.core.JsonStreamContext; | |
9 | 10 | import com.fasterxml.jackson.core.JsonToken; |
10 | 11 | |
11 | 12 | public abstract class AsyncReaderWrapper |
52 | 53 | |
53 | 54 | public abstract JsonToken nextToken() throws IOException; |
54 | 55 | |
56 | public JsonStreamContext getParsingContext() { | |
57 | return _streamReader.getParsingContext(); | |
58 | } | |
59 | ||
55 | 60 | public int getIntValue() throws IOException { return _streamReader.getIntValue(); } |
56 | 61 | public long getLongValue() throws IOException { return _streamReader.getLongValue(); } |
57 | 62 | public float getFloatValue() throws IOException { return _streamReader.getFloatValue(); } |
6 | 6 | /** |
7 | 7 | * Unit tests for class {@link RequestPayload}. |
8 | 8 | * |
9 | * @date 2017-07-31 | |
10 | 9 | * @see RequestPayload |
11 | 10 | **/ |
12 | 11 | public class RequestPayloadTest { |
13 | ||
12 | @SuppressWarnings("unused") | |
14 | 13 | @Test(expected = IllegalArgumentException.class) |
15 | 14 | public void testFailsToCreateTakingCharSequenceThrowsIllegalArgumentExceptionOne() { |
16 | new RequestPayload(null); | |
15 | new RequestPayload(null); | |
17 | 16 | } |
18 | 17 | |
18 | @SuppressWarnings("unused") | |
19 | 19 | @Test(expected = IllegalArgumentException.class) |
20 | 20 | public void testFailsToCreateTakingCharSequenceThrowsIllegalArgumentExceptionTwo() { |
21 | new RequestPayload(null, "UTF-8"); | |
21 | new RequestPayload(null, "UTF-8"); | |
22 | 22 | } |
23 | 23 | |
24 | 24 | @Test |
158 | 158 | } |
159 | 159 | return sw.toString(); |
160 | 160 | } |
161 | ||
162 | // [core#502]: Force sub-classes to reimplement `createInstance` | |
163 | public void testInvalidSubClass() throws Exception | |
164 | { | |
165 | DefaultPrettyPrinter pp = new MyPrettyPrinter(); | |
166 | try { | |
167 | pp.createInstance(); | |
168 | fail("Should not pass"); | |
169 | } catch (IllegalStateException e) { | |
170 | verifyException(e, "does not override"); | |
171 | } | |
172 | } | |
173 | ||
174 | @SuppressWarnings("serial") | |
175 | static class MyPrettyPrinter extends DefaultPrettyPrinter { } | |
161 | 176 | } |
28 | 28 | return null; |
29 | 29 | } |
30 | 30 | @Override |
31 | public <T> T readValue(JsonParser p, TypeReference<?> valueTypeRef) { | |
31 | public <T> T readValue(JsonParser p, TypeReference<T> valueTypeRef) { | |
32 | 32 | return null; |
33 | 33 | } |
34 | 34 | @Override |
41 | 41 | } |
42 | 42 | @Override |
43 | 43 | public <T> Iterator<T> readValues(JsonParser p, |
44 | TypeReference<?> valueTypeRef) throws IOException { | |
44 | TypeReference<T> valueTypeRef) throws IOException { | |
45 | 45 | return null; |
46 | 46 | } |
47 | 47 | @Override |
70 | 70 | } |
71 | 71 | @Override |
72 | 72 | public TreeNode createArrayNode() { |
73 | return null; | |
74 | } | |
75 | @Override | |
76 | public TreeNode missingNode() { | |
77 | return null; | |
78 | } | |
79 | ||
80 | @Override | |
81 | public TreeNode nullNode() { | |
73 | 82 | return null; |
74 | 83 | } |
75 | 84 | @Override |
101 | 101 | public void testGetCurrentSegment() { |
102 | 102 | TextBuffer textBuffer = new TextBuffer(null); |
103 | 103 | textBuffer.emptyAndGetCurrentSegment(); |
104 | textBuffer.setCurrentAndReturn(1000); | |
104 | // 26-Aug-2019, tatu: Value depends on "minimum segment size": | |
105 | textBuffer.setCurrentAndReturn(500); | |
105 | 106 | textBuffer.getCurrentSegment(); |
106 | 107 | |
107 | assertEquals(1000, textBuffer.size()); | |
108 | assertEquals(500, textBuffer.size()); | |
108 | 109 | } |
109 | 110 | |
110 | 111 | public void testAppendTakingTwoAndThreeInts() { |
0 | package com.fasterxml.jackson.failing; | |
1 | ||
2 | import com.fasterxml.jackson.core.JsonLocation; | |
3 | import com.fasterxml.jackson.core.JsonParser; | |
4 | import com.fasterxml.jackson.core.JsonToken; | |
5 | ||
6 | public class LocationOffsets455Test extends com.fasterxml.jackson.core.BaseTest | |
7 | { | |
8 | // for [jackson-core#455] | |
9 | public void testEOFLocationViaReader() throws Exception | |
10 | { | |
11 | JsonParser p = createParserUsingReader("42"); | |
12 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
13 | assertEquals(42, p.getIntValue()); | |
14 | JsonLocation loc = p.getCurrentLocation(); | |
15 | assertEquals(1, loc.getLineNr()); | |
16 | assertEquals(3, loc.getColumnNr()); | |
17 | assertEquals(2, loc.getByteOffset()); | |
18 | ||
19 | assertNull(p.nextToken()); | |
20 | ||
21 | loc = p.getCurrentLocation(); | |
22 | System.err.println("LOC/r = "+loc); | |
23 | assertEquals(1, loc.getLineNr()); | |
24 | assertEquals(2, loc.getByteOffset()); | |
25 | assertEquals(3, loc.getColumnNr()); | |
26 | p.close(); | |
27 | } | |
28 | ||
29 | // for [jackson-core#455] | |
30 | public void testEOFLocationViaStream() throws Exception | |
31 | { | |
32 | JsonParser p = createParserUsingStream("42", "UTF-8"); | |
33 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); | |
34 | assertEquals(42, p.getIntValue()); | |
35 | JsonLocation loc = p.getCurrentLocation(); | |
36 | assertEquals(1, loc.getLineNr()); | |
37 | assertEquals(3, loc.getColumnNr()); | |
38 | assertEquals(2, loc.getByteOffset()); | |
39 | ||
40 | assertNull(p.nextToken()); | |
41 | loc = p.getCurrentLocation(); | |
42 | System.err.println("LOC/str = "+loc); | |
43 | assertEquals(1, loc.getLineNr()); | |
44 | assertEquals(2, loc.getCharOffset()); | |
45 | assertEquals(3, loc.getColumnNr()); | |
46 | p.close(); | |
47 | } | |
48 | ||
49 | } |
0 | package com.fasterxml.jackson.failing; | |
1 | ||
2 | import com.fasterxml.jackson.core.*; | |
3 | ||
4 | // For checking [databind#533] | |
5 | public class LocationOffsets533Test extends com.fasterxml.jackson.core.BaseTest | |
6 | { | |
7 | final JsonFactory JSON_F = new JsonFactory(); | |
8 | ||
9 | public void testOffsetWithoutInputOffset() throws Exception | |
10 | { | |
11 | JsonLocation loc; | |
12 | JsonParser p; | |
13 | // 3 spaces before, 2 after, just for padding | |
14 | byte[] b = " { } ".getBytes("UTF-8"); | |
15 | ||
16 | // and then peel them off | |
17 | p = JSON_F.createParser(/*ObjectReadContext.empty(),*/ b); | |
18 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
19 | ||
20 | loc = p.getTokenLocation(); | |
21 | assertEquals(3L, loc.getByteOffset()); | |
22 | assertEquals(-1L, loc.getCharOffset()); | |
23 | assertEquals(1, loc.getLineNr()); | |
24 | assertEquals(4, loc.getColumnNr()); | |
25 | ||
26 | loc = p.getCurrentLocation(); | |
27 | assertEquals(4L, loc.getByteOffset()); | |
28 | assertEquals(-1L, loc.getCharOffset()); | |
29 | assertEquals(1, loc.getLineNr()); | |
30 | assertEquals(5, loc.getColumnNr()); | |
31 | ||
32 | p.close(); | |
33 | } | |
34 | ||
35 | // for [core#533] | |
36 | public void testUtf8Bom() throws Exception | |
37 | { | |
38 | JsonLocation loc; | |
39 | JsonParser p; | |
40 | ||
41 | byte[] b = withUtf8Bom("{ }".getBytes()); | |
42 | ||
43 | // and then peel them off | |
44 | p = JSON_F.createParser(/*ObjectReadContext.empty(),*/ b); | |
45 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
46 | ||
47 | loc = p.getTokenLocation(); | |
48 | assertEquals(3L, loc.getByteOffset()); | |
49 | assertEquals(-1L, loc.getCharOffset()); | |
50 | assertEquals(1, loc.getLineNr()); | |
51 | assertEquals(4, loc.getColumnNr()); | |
52 | ||
53 | loc = p.getCurrentLocation(); | |
54 | assertEquals(4L, loc.getByteOffset()); | |
55 | assertEquals(-1L, loc.getCharOffset()); | |
56 | assertEquals(1, loc.getLineNr()); | |
57 | assertEquals(5, loc.getColumnNr()); | |
58 | ||
59 | p.close(); | |
60 | } | |
61 | ||
62 | public void testUtf8BomWithPadding() throws Exception | |
63 | { | |
64 | JsonLocation loc; | |
65 | JsonParser p; | |
66 | ||
67 | byte[] b = withUtf8Bom(" { }".getBytes()); | |
68 | ||
69 | // and then peel them off | |
70 | p = JSON_F.createParser(/*ObjectReadContext.empty(),*/ b); | |
71 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
72 | ||
73 | loc = p.getTokenLocation(); | |
74 | assertEquals(6L, loc.getByteOffset()); | |
75 | assertEquals(-1L, loc.getCharOffset()); | |
76 | assertEquals(1, loc.getLineNr()); | |
77 | assertEquals(7, loc.getColumnNr()); | |
78 | ||
79 | loc = p.getCurrentLocation(); | |
80 | assertEquals(7L, loc.getByteOffset()); | |
81 | assertEquals(-1L, loc.getCharOffset()); | |
82 | assertEquals(1, loc.getLineNr()); | |
83 | assertEquals(8, loc.getColumnNr()); | |
84 | ||
85 | p.close(); | |
86 | } | |
87 | ||
88 | public void testUtf8BomWithInputOffset() throws Exception | |
89 | { | |
90 | JsonLocation loc; | |
91 | JsonParser p; | |
92 | ||
93 | byte[] b = withUtf8Bom(" { }".getBytes()); | |
94 | ||
95 | // and then peel them off | |
96 | p = JSON_F.createParser(/*ObjectReadContext.empty(),*/ b); | |
97 | assertToken(JsonToken.START_OBJECT, p.nextToken()); | |
98 | ||
99 | loc = p.getTokenLocation(); | |
100 | assertEquals(6L, loc.getByteOffset()); | |
101 | assertEquals(-1L, loc.getCharOffset()); | |
102 | assertEquals(1, loc.getLineNr()); | |
103 | assertEquals(7, loc.getColumnNr()); | |
104 | ||
105 | loc = p.getCurrentLocation(); | |
106 | assertEquals(7L, loc.getByteOffset()); | |
107 | assertEquals(-1L, loc.getCharOffset()); | |
108 | assertEquals(1, loc.getLineNr()); | |
109 | assertEquals(8, loc.getColumnNr()); | |
110 | ||
111 | p.close(); | |
112 | } | |
113 | ||
114 | private byte[] withUtf8Bom(byte[] bytes) { | |
115 | byte[] arr = new byte[bytes.length + 3]; | |
116 | // write UTF-8 BOM | |
117 | arr[0] = (byte) 0xEF; | |
118 | arr[1] = (byte) 0xBB; | |
119 | arr[2] = (byte) 0xBF; | |
120 | System.arraycopy(bytes, 0, arr, 3, bytes.length); | |
121 | return arr; | |
122 | } | |
123 | } |