andrew.asa
7 years ago
496 changed files with 111483 additions and 0 deletions
Binary file not shown.
@ -0,0 +1,8 @@ |
|||||||
|
This copy of Jackson JSON processor annotations is licensed under the |
||||||
|
Apache (Software) License, version 2.0 ("the License"). |
||||||
|
See the License for details about distribution rights, and the |
||||||
|
specific rights regarding derivate works. |
||||||
|
|
||||||
|
You may obtain a copy of the License at: |
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
@ -0,0 +1,20 @@ |
|||||||
|
# Jackson JSON processor |
||||||
|
|
||||||
|
Jackson is a high-performance, Free/Open Source JSON processing library. |
||||||
|
It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has |
||||||
|
been in development since 2007. |
||||||
|
It is currently developed by a community of developers, as well as supported |
||||||
|
commercially by FasterXML.com. |
||||||
|
|
||||||
|
## Licensing |
||||||
|
|
||||||
|
Jackson core and extension components may be licensed under different licenses. |
||||||
|
To find the details that apply to this artifact see the accompanying LICENSE file. |
||||||
|
For more information, including possible other licensing options, contact |
||||||
|
FasterXML.com (http://fasterxml.com). |
||||||
|
|
||||||
|
## Credits |
||||||
|
|
||||||
|
A list of contributors may be found from CREDITS file, which is included |
||||||
|
in some artifacts (usually source distributions); but is always available |
||||||
|
from the source code management (SCM) system project uses. |
@ -0,0 +1 @@ |
|||||||
|
com.fr.third.fasterxml.jackson.core.JsonFactory |
@ -0,0 +1 @@ |
|||||||
|
com.fr.third.fasterxml.jackson.databind.ObjectMapper |
@ -0,0 +1,20 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Meta-annotation (annotations used on other annotations) |
||||||
|
* used for marking all annotations that are |
||||||
|
* part of Jackson package. Can be used for recognizing all |
||||||
|
* Jackson annotations generically, and in future also for |
||||||
|
* passing other generic annotation configuration. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public @interface JacksonAnnotation |
||||||
|
{ |
||||||
|
// for now, a pure tag annotation, no parameters
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Meta-annotation (annotations used on other annotations) |
||||||
|
* used for indicating that instead of using target annotation |
||||||
|
* (annotation annotated with this annotation), |
||||||
|
* Jackson should use meta-annotations it has. |
||||||
|
* This can be useful in creating "combo-annotations" by having |
||||||
|
* a container annotation, which needs to be annotated with this |
||||||
|
* annotation as well as all annotations it 'contains'. |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JacksonAnnotationsInside |
||||||
|
{ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.annotation.JacksonAnnotation; |
||||||
|
|
||||||
|
/** |
||||||
|
* Jackson-specific annotation used for indicating that value of |
||||||
|
* annotated property will be "injected", i.e. set based on value |
||||||
|
* configured by <code>ObjectMapper</code> (usually on per-call basis). |
||||||
|
* Usually property is not deserialized from JSON, although it possible |
||||||
|
* to have injected value as default and still allow optional override |
||||||
|
* from JSON. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JacksonInject |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Logical id of the value to inject; if not specified (or specified |
||||||
|
* as empty String), will use id based on declared type of property. |
||||||
|
*/ |
||||||
|
public String value() default ""; |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that can be used to define a non-static, |
||||||
|
* no-argument method or member field as something of a reverse of |
||||||
|
* {@link JsonAnySetter} method; basically being used like a |
||||||
|
* getter but such that contents of the returned Map (type <b>must</b> be |
||||||
|
* {@link java.util.Map}) are serialized as if they were actual properties |
||||||
|
* of the bean that contains method/field with this annotations. |
||||||
|
* As with {@link JsonAnySetter}, only one property should be annotated |
||||||
|
* with this annotation. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonAnyGetter |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that can be used to define a non-static, |
||||||
|
* two-argument method (first argument name of property, second value |
||||||
|
* to set), to be used as a "fallback" handler |
||||||
|
* for all otherwise unrecognized properties found from JSON content. |
||||||
|
* It is similar to {@link javax.xml.bind.annotation.XmlAnyElement} |
||||||
|
* in behavior; and can only be used to denote a single property |
||||||
|
* per type. |
||||||
|
*<p> |
||||||
|
* If used, all otherwise unmapped key-value pairs from JSON Object values |
||||||
|
* are added to the property (of type Map or bean). |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonAnySetter |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
import java.lang.reflect.Member; |
||||||
|
import java.lang.reflect.Modifier; |
||||||
|
|
||||||
|
/** |
||||||
|
* Class annotation that can be used to define which kinds of Methods |
||||||
|
* are to be detected by auto-detection. |
||||||
|
* Auto-detection means using name conventions |
||||||
|
* and/or signature templates to find methods to use for data binding. |
||||||
|
* For example, so-called "getters" can be auto-detected by looking for |
||||||
|
* public member methods that return a value, do not take argument, |
||||||
|
* and have prefix "get" in their name. |
||||||
|
*<p> |
||||||
|
* Pseudo-value <code>NONE</code> means that all auto-detection is disabled |
||||||
|
* for the <b>specific</b> class that annotation is applied to (including |
||||||
|
* its super-types, but only when resolving that class). |
||||||
|
* Pseudo-value <code>ALWAYS</code> means that auto-detection is enabled |
||||||
|
* for all method types for the class in similar way. |
||||||
|
*<p> |
||||||
|
* The default value is <code>ALWAYS</code>: that is, by default, auto-detection |
||||||
|
* is enabled for all classes unless instructed otherwise. |
||||||
|
*<p> |
||||||
|
* Starting with version 1.5, it is also possible to use more fine-grained |
||||||
|
* definitions, to basically define minimum visibility level needed. Defaults |
||||||
|
* are different for different types (getters need to be public; setters can |
||||||
|
* have any access modifier, for example). |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonAutoDetect |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Enumeration for possible visibility thresholds (minimum visibility) |
||||||
|
* that can be used to limit which methods (and fields) are |
||||||
|
* auto-detected. |
||||||
|
*/ |
||||||
|
public enum Visibility { |
||||||
|
/** |
||||||
|
* Value that means that all kinds of access modifiers are acceptable, |
||||||
|
* from private to public. |
||||||
|
*/ |
||||||
|
ANY, |
||||||
|
/** |
||||||
|
* Value that means that any other access modifier other than 'private' |
||||||
|
* is considered auto-detectable. |
||||||
|
*/ |
||||||
|
NON_PRIVATE, |
||||||
|
/** |
||||||
|
* Value that means access modifiers 'protected' and 'public' are |
||||||
|
* auto-detectable (and 'private' and "package access" == no modifiers |
||||||
|
* are not) |
||||||
|
*/ |
||||||
|
PROTECTED_AND_PUBLIC, |
||||||
|
/** |
||||||
|
* Value to indicate that only 'public' access modifier is considered |
||||||
|
* auto-detectable. |
||||||
|
*/ |
||||||
|
PUBLIC_ONLY, |
||||||
|
/** |
||||||
|
* Value that indicates that no access modifiers are auto-detectable: |
||||||
|
* this can be used to explicitly disable auto-detection for specified |
||||||
|
* types. |
||||||
|
*/ |
||||||
|
NONE, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that default visibility level (whatever it is, |
||||||
|
* depends on context) is to be used. This usually means that inherited |
||||||
|
* value (from parent visibility settings) is to be used. |
||||||
|
*/ |
||||||
|
DEFAULT; |
||||||
|
|
||||||
|
public boolean isVisible(Member m) { |
||||||
|
switch (this) { |
||||||
|
case ANY: |
||||||
|
return true; |
||||||
|
case NONE: |
||||||
|
return false; |
||||||
|
case NON_PRIVATE: |
||||||
|
return !Modifier.isPrivate(m.getModifiers()); |
||||||
|
case PROTECTED_AND_PUBLIC: |
||||||
|
if (Modifier.isProtected(m.getModifiers())) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
// fall through to public case:
|
||||||
|
case PUBLIC_ONLY: |
||||||
|
return Modifier.isPublic(m.getModifiers()); |
||||||
|
default: |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Minimum visibility required for auto-detecting regular getter methods. |
||||||
|
*/ |
||||||
|
Visibility getterVisibility() default Visibility.DEFAULT; |
||||||
|
|
||||||
|
/** |
||||||
|
* Minimum visibility required for auto-detecting is-getter methods. |
||||||
|
*/ |
||||||
|
Visibility isGetterVisibility() default Visibility.DEFAULT; |
||||||
|
|
||||||
|
/** |
||||||
|
* Minimum visibility required for auto-detecting setter methods. |
||||||
|
*/ |
||||||
|
Visibility setterVisibility() default Visibility.DEFAULT; |
||||||
|
|
||||||
|
/** |
||||||
|
* Minimum visibility required for auto-detecting Creator methods, |
||||||
|
* except for no-argument constructors (which are always detected |
||||||
|
* no matter what). |
||||||
|
*/ |
||||||
|
Visibility creatorVisibility() default Visibility.DEFAULT; |
||||||
|
|
||||||
|
/** |
||||||
|
* Minimum visibility required for auto-detecting member fields. |
||||||
|
*/ |
||||||
|
Visibility fieldVisibility() default Visibility.DEFAULT; |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used to indicate that associated property is part of |
||||||
|
* two-way linkage between fields; and that its role is "child" (or "back") link. |
||||||
|
* Value type of the property must be a bean: it can not be a Collection, Map, |
||||||
|
* Array or enumeration. |
||||||
|
* Linkage is handled such that the property |
||||||
|
* annotated with this annotation is not serialized; and during deserialization, |
||||||
|
* its value is set to instance that has the "managed" (forward) link. |
||||||
|
*<p> |
||||||
|
* All references have logical name to allow handling multiple linkages; typical case |
||||||
|
* would be that where nodes have both parent/child and sibling linkages. If so, |
||||||
|
* pairs of references should be named differently. |
||||||
|
* It is an error for a class to have multiple back references with same name, |
||||||
|
* even if types pointed are different. |
||||||
|
*<p> |
||||||
|
* Note: only methods and fields can be annotated with this annotation: constructor |
||||||
|
* arguments should NOT be annotated, as they can not be either managed or back |
||||||
|
* references. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonBackReference |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Logical have for the reference property pair; used to link managed and |
||||||
|
* back references. Default name can be used if there is just single |
||||||
|
* reference pair (for example, node class that just has parent/child linkage, |
||||||
|
* consisting of one managed reference and matching back reference) |
||||||
|
*/ |
||||||
|
public String value() default "defaultReference"; |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that can be used to define constructors and factory |
||||||
|
* methods as one to use for instantiating new instances of the associated |
||||||
|
* class. |
||||||
|
*<p> |
||||||
|
* NOTE: when annotating creator methods (constructors, factory methods), |
||||||
|
* method must either be: |
||||||
|
*<ul> |
||||||
|
* <li>Single-argument constructor/factory method without {@link JsonProperty} |
||||||
|
* annotation for the argument: if so, this is so-called "delegate creator", |
||||||
|
* in which case Jackson first binds JSON into type of the argument, and |
||||||
|
* then calls creator |
||||||
|
* </li> |
||||||
|
* <li>Constructor/factory method where <b>every argument</b> is annotated with |
||||||
|
* either {@link JsonProperty} or {@link JacksonInject}, to indicate name |
||||||
|
* of property to bind to |
||||||
|
* </li> |
||||||
|
* </ul> |
||||||
|
* Also note that all {@link JsonProperty} annotations MUST use actual name |
||||||
|
* (NOT empty String for "default"): this because Java bytecode does not |
||||||
|
* retain names of method or constructor arguments. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonCreator |
||||||
|
{ |
||||||
|
// no values, since there's no property
|
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used to indicate which logical filter is to be used |
||||||
|
* for filtering out properties of type (class) annotated; |
||||||
|
* association made by this annotation declaring ids of filters, |
||||||
|
* and <code>com.fr.third.fasterxml.jackson.databind.ObjectMapper</code> (or objects |
||||||
|
* it delegates to) providing matching filters by id. |
||||||
|
*<p> |
||||||
|
* Filters to use are usually of type |
||||||
|
* <code>com.fr.third.fasterxml.jackson.databind.ser.BeanPropertyFilter</code> and |
||||||
|
* are registered through <code>com.fr.third.fasterxml.jackson.databind.ObjectMapper</code> |
||||||
|
*<p> |
||||||
|
* Since 2.3, this annotation can also be used on properties (fields, methods, |
||||||
|
* constructor parameters). |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, |
||||||
|
ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER // new in 2.3
|
||||||
|
}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@com.fr.third.fasterxml.jackson.annotation.JacksonAnnotation |
||||||
|
public @interface JsonFilter |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Id of filter to use; if empty String (""), no filter is to be used. |
||||||
|
*/ |
||||||
|
public String value(); |
||||||
|
} |
@ -0,0 +1,252 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
import java.util.Locale; |
||||||
|
import java.util.TimeZone; |
||||||
|
|
||||||
|
/** |
||||||
|
* General-purpose annotation used for configuring details of how |
||||||
|
* values of properties are to be serialized. |
||||||
|
* Unlike most other Jackson annotations, annotation does not |
||||||
|
* have specific universal interpretation: instead, effect depends on datatype |
||||||
|
* of property being annotated (or more specifically, deserializer |
||||||
|
* and serializer being used). |
||||||
|
*<p> |
||||||
|
* Common uses include choosing between alternate representations -- for example, |
||||||
|
* whether {@link java.util.Date} is to be serialized as number (Java timestamp) |
||||||
|
* or String (such as ISO-8601 compatible time value) -- as well as configuring |
||||||
|
* exact details with {@link #pattern} property. |
||||||
|
*<p> |
||||||
|
* As of Jackson 2.1, known special handling include: |
||||||
|
*<ul> |
||||||
|
* <li>{@link java.util.Date}: Shape can be {@link Shape#STRING} or {@link Shape#NUMBER}; |
||||||
|
* pattern may contain {@link java.text.SimpleDateFormat}-compatible pattern definition. |
||||||
|
* </li> |
||||||
|
*</ul> |
||||||
|
* Jackson 2.1 added following new features: |
||||||
|
*<ul> |
||||||
|
* <li>Can now be used on Classes (types) as well, for modified default behavior, possibly |
||||||
|
* overridden by per-property annotation |
||||||
|
* </li> |
||||||
|
* <li>{@link java.lang.Enum}s: Shapes {@link Shape#STRING} and {@link Shape#NUMBER} can be |
||||||
|
* used to change between numeric (index) and textual (name or <code>toString()</code>); |
||||||
|
* but it is also possible to use {@link Shape#OBJECT} to serialize (but not deserialize) |
||||||
|
* {@link java.lang.Enum}s as JSON Objects (as if they were POJOs). NOTE: serialization |
||||||
|
* as JSON Object only works with class annotation; |
||||||
|
* will not work as per-property annotation. |
||||||
|
* </li> |
||||||
|
* <li>{@link java.util.Collection}s can be serialized as (and deserialized from) JSON Objects, |
||||||
|
* if {@link Shape#OBJECT} is used. NOTE: can ONLY be used as class annotation; |
||||||
|
* will not work as per-property annotation. |
||||||
|
* </li> |
||||||
|
*</ul> |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, |
||||||
|
ElementType.TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonFormat |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Value that indicates that default {@link java.util.Locale} |
||||||
|
* (from deserialization or serialization context) should be used: |
||||||
|
* annotation does not define value to use. |
||||||
|
*/ |
||||||
|
public final static String DEFAULT_LOCALE = "##default"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that default {@link java.util.TimeZone} |
||||||
|
* (from deserialization or serialization context) should be used: |
||||||
|
* annotation does not define value to use. |
||||||
|
*/ |
||||||
|
public final static String DEFAULT_TIMEZONE = "##default"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Datatype-specific additional piece of configuration that may be used |
||||||
|
* to further refine formatting aspects. This may, for example, determine |
||||||
|
* low-level format String used for {@link java.util.Date} serialization; |
||||||
|
* however, exact use is determined by specific <code>JsonSerializer</code> |
||||||
|
*/ |
||||||
|
public String pattern() default ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* Structure to use for serialization: definition of mapping depends on datatype, |
||||||
|
* but usually has straight-forward counterpart in data format (JSON). |
||||||
|
* Note that commonly only a subset of shapes is available; and if 'invalid' value |
||||||
|
* is chosen, defaults are usually used. |
||||||
|
*/ |
||||||
|
public Shape shape() default Shape.ANY; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link java.util.Locale} to use for serialization (if needed). |
||||||
|
* Special value of {@link #DEFAULT_LOCALE} |
||||||
|
* can be used to mean "just use the default", where default is specified |
||||||
|
* by the serialization context, which in turn defaults to system |
||||||
|
* defaults ({@link java.util.Locale#getDefault()}) unless explicitly |
||||||
|
* set to another locale. |
||||||
|
*/ |
||||||
|
public String locale() default DEFAULT_LOCALE; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link java.util.TimeZone} to use for serialization (if needed). |
||||||
|
* Special value of {@link #DEFAULT_TIMEZONE} |
||||||
|
* can be used to mean "just use the default", where default is specified |
||||||
|
* by the serialization context, which in turn defaults to system |
||||||
|
* defaults ({@link java.util.TimeZone#getDefault()}) unless explicitly |
||||||
|
* set to another locale. |
||||||
|
*/ |
||||||
|
public String timezone() default DEFAULT_TIMEZONE; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Value enumeration(s), value class(es) |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Value enumeration used for indicating preferred Shape; translates |
||||||
|
* loosely to JSON types, with some extra values to indicate less precise |
||||||
|
* choices (i.e. allowing one of multiple actual shapes) |
||||||
|
*/ |
||||||
|
public enum Shape |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Marker enum value that indicates "default" (or "whatever") choice; needed |
||||||
|
* since Annotations can not have null values for enums. |
||||||
|
*/ |
||||||
|
ANY, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates shape should not be structural (that is, not |
||||||
|
* {@link #ARRAY} or {@link #OBJECT}, but can be any other shape. |
||||||
|
*/ |
||||||
|
SCALAR, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that (JSON) Array type should be used. |
||||||
|
*/ |
||||||
|
ARRAY, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that (JSON) Object type should be used. |
||||||
|
*/ |
||||||
|
OBJECT, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that a numeric (JSON) type should be used |
||||||
|
* (but does not specify whether integer or floating-point representation |
||||||
|
* should be used) |
||||||
|
*/ |
||||||
|
NUMBER, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that floating-point numeric type should be used |
||||||
|
*/ |
||||||
|
NUMBER_FLOAT, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that integer number type should be used |
||||||
|
* (and not {@link #NUMBER_FLOAT}). |
||||||
|
*/ |
||||||
|
NUMBER_INT, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that (JSON) String type should be used. |
||||||
|
*/ |
||||||
|
STRING, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that (JSON) boolean type |
||||||
|
* (true, false) should be used. |
||||||
|
*/ |
||||||
|
BOOLEAN |
||||||
|
; |
||||||
|
|
||||||
|
public boolean isNumeric() { |
||||||
|
return (this == NUMBER) || (this == NUMBER_INT) || (this == NUMBER_FLOAT); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isStructured() { |
||||||
|
return (this == OBJECT) || (this == ARRAY); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper class used to contain information from a single {@link JsonFormat} |
||||||
|
* annotation. |
||||||
|
*/ |
||||||
|
public static class Value |
||||||
|
{ |
||||||
|
private final String pattern; |
||||||
|
private final Shape shape; |
||||||
|
private final Locale locale; |
||||||
|
private final TimeZone timezone; |
||||||
|
|
||||||
|
public Value() { |
||||||
|
this("", Shape.ANY, "", ""); |
||||||
|
} |
||||||
|
|
||||||
|
public Value(JsonFormat ann) { |
||||||
|
this(ann.pattern(), ann.shape(), ann.locale(), ann.timezone()); |
||||||
|
} |
||||||
|
|
||||||
|
public Value(String p, Shape sh, String localeStr, String tzStr) |
||||||
|
{ |
||||||
|
this(p, sh |
||||||
|
,(localeStr == null || localeStr.length() == 0 || DEFAULT_LOCALE.equals(localeStr)) ? |
||||||
|
null : new Locale(localeStr) |
||||||
|
,(tzStr == null || tzStr.length() == 0 || DEFAULT_TIMEZONE.equals(tzStr)) ? |
||||||
|
null : TimeZone.getTimeZone(tzStr) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public Value(String p, Shape sh, Locale l, TimeZone tz) |
||||||
|
{ |
||||||
|
pattern = p; |
||||||
|
shape = sh; |
||||||
|
locale = l; |
||||||
|
timezone = tz; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public Value withPattern(String p) { |
||||||
|
return new Value(p, shape, locale, timezone); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public Value withShape(Shape s) { |
||||||
|
return new Value(pattern, s, locale, timezone); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public Value withLocale(Locale l) { |
||||||
|
return new Value(pattern, shape, l, timezone); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public Value withTimeZone(TimeZone tz) { |
||||||
|
return new Value(pattern, shape, locale, tz); |
||||||
|
} |
||||||
|
|
||||||
|
public String getPattern() { return pattern; } |
||||||
|
public Shape getShape() { return shape; } |
||||||
|
public Locale getLocale() { return locale; } |
||||||
|
public TimeZone getTimeZone() { return timezone; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that can be used to define a non-static, |
||||||
|
* no-argument value-returning (non-void) method to be used as a "getter" |
||||||
|
* for a logical property. |
||||||
|
* It can be used as an alternative to more general |
||||||
|
* {@link JsonProperty} annotation (which is the recommended choice in |
||||||
|
* general case). |
||||||
|
*<p> |
||||||
|
* Getter means that when serializing Object instance of class that has |
||||||
|
* this method (possibly inherited from a super class), a call is made |
||||||
|
* through the method, and return value will be serialized as value of |
||||||
|
* the property. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonGetter |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Defines name of the logical property this |
||||||
|
* method is used to access ("get"); empty String means that |
||||||
|
* name should be derived from the underlying method (using |
||||||
|
* standard Bean name detection rules) |
||||||
|
*/ |
||||||
|
String value() default ""; |
||||||
|
} |
@ -0,0 +1,75 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used for indicating that values of annotated type |
||||||
|
* or property should be serializing so that instances either |
||||||
|
* contain additional object identifier (in addition actual object |
||||||
|
* properties), or as a reference that consists of an object id |
||||||
|
* that refers to a full serialization. In practice this is done |
||||||
|
* by serializing the first instance as full object and object |
||||||
|
* identity, and other references to the object as reference values. |
||||||
|
*<p> |
||||||
|
* There are two main approaches to generating object identifier: |
||||||
|
* either using a generator (either one of standard ones, or a custom |
||||||
|
* generator), or using a value of a property. The latter case is |
||||||
|
* indicated by using a placeholder generator marker |
||||||
|
* {@link ObjectIdGenerators.PropertyGenerator}; former by using explicit generator. |
||||||
|
* Object id has to be serialized as a property in case of POJOs; |
||||||
|
* object identity is currently NOT support for JSON Array types |
||||||
|
* (Java arrays or Lists) or Java Map types. |
||||||
|
*<p> |
||||||
|
* Finally, note that generator type of {@link ObjectIdGenerators.None} |
||||||
|
* indicates that no Object Id should be included or used: it is included |
||||||
|
* to allow suppressing Object Ids using mix-in annotations. |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, |
||||||
|
ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonIdentityInfo |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Name of JSON property in which Object Id will reside: also, |
||||||
|
* if "from property" marker generator is used, identifies |
||||||
|
* property that will be accessed to get type id. |
||||||
|
* If a property is used, name must match its external |
||||||
|
* name (one defined by annotation, or derived from accessor |
||||||
|
* name as per Java Bean Introspection rules). |
||||||
|
*<p> |
||||||
|
* Default value is <code>@id</code>. |
||||||
|
*/ |
||||||
|
public String property() default "@id"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Generator to use for producing Object Identifier for objects: |
||||||
|
* either one of pre-defined generators from |
||||||
|
* {@link ObjectIdGenerator}, or a custom generator. |
||||||
|
* Defined as class to instantiate. |
||||||
|
*<p> |
||||||
|
* Note that special type |
||||||
|
* {@link ObjectIdGenerators.None} |
||||||
|
* can be used to disable inclusion of Object Ids. |
||||||
|
*/ |
||||||
|
public Class<? extends ObjectIdGenerator<?>> generator(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Scope is used to define applicability of an Object Id: all ids |
||||||
|
* must be unique within their scope; where scope is defined |
||||||
|
* as combination of this value and generator type. |
||||||
|
* Comparison is simple equivalence, meaning that both type |
||||||
|
* generator type and scope class must be the same. |
||||||
|
*<p> |
||||||
|
* Scope is used for determining how many generators are needed; |
||||||
|
* more than one scope is typically only needed if external Object Ids |
||||||
|
* have overlapping value domains (i.e. are only unique within some |
||||||
|
* limited scope) |
||||||
|
*/ |
||||||
|
public Class<?> scope() default Object.class; |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Optional annotation that can be used for customizing details of a reference |
||||||
|
* to Objects for which "Object Identity" is enabled (see {@link JsonIdentityInfo}). |
||||||
|
* The main use case is that of enforcing use of Object Id even for the first |
||||||
|
* time an Object is referenced, instead of first instance being serialized |
||||||
|
* as full POJO. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, |
||||||
|
ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonIdentityReference |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Marker to indicate whether all referenced values are to |
||||||
|
* be serialized as ids (true); or by serializing the |
||||||
|
* first encountered reference as POJO and only then as id (false). |
||||||
|
*<p> |
||||||
|
* Note that if value of 'true' is used, deserialization may require |
||||||
|
* additional contextual information, and possibly using a custom |
||||||
|
* id resolver -- the default handling may not be sufficient. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public boolean alwaysAsId() default false; |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that indicates that the annotated method or field is to be |
||||||
|
* ignored by introspection-based |
||||||
|
* serialization and deserialization functionality. That is, it should |
||||||
|
* not be consider a "getter", "setter" or "creator". |
||||||
|
*<p> |
||||||
|
* In addition, starting with Jackson 1.9, if this is the only annotation |
||||||
|
* associated with a property, it will also cause cause the whole |
||||||
|
* property to be ignored: that is, if setter has this annotation and |
||||||
|
* getter has no annotations, getter is also effectively ignored. |
||||||
|
* It is still possible for different accessors to use different |
||||||
|
* annotations; so if only "getter" is to be ignored, other accessors |
||||||
|
* (setter or field) would need explicit annotation to prevent |
||||||
|
* ignoral (usually {@link JsonProperty}). |
||||||
|
* <p> |
||||||
|
* For example, a "getter" method that would otherwise denote |
||||||
|
* a property (like, say, "getValue" to suggest property "value") |
||||||
|
* to serialize, would be ignored and no such property would |
||||||
|
* be output unless another annotation defines alternative method to use. |
||||||
|
*<p> |
||||||
|
* Before version 1.9, this annotation worked purely on method-by-method (or field-by-field) |
||||||
|
* basis; annotation on one method or field did not imply ignoring other methods |
||||||
|
* or fields. However, with version 1.9 and above, annotations associated |
||||||
|
* with various accessors (getter, setter, field, constructor parameter) of |
||||||
|
* a logical property are combined; meaning that annotations in one (say, setter) |
||||||
|
* can have effects on all of them (if getter or field has nothing indicating |
||||||
|
* otherwise). |
||||||
|
*<p> |
||||||
|
* Annotation is usually used just a like a marker annotation, that |
||||||
|
* is, without explicitly defining 'value' argument (which defaults |
||||||
|
* to <code>true</code>): but argument can be explicitly defined. |
||||||
|
* This can be done to override an existing JsonIgnore by explicitly |
||||||
|
* defining one with 'false' argument. |
||||||
|
*<p> |
||||||
|
* Annotation is similar to {@link javax.xml.bind.annotation.XmlTransient} |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonIgnore |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Optional argument that defines whether this annotation is active |
||||||
|
* or not. The only use for value 'false' if for overriding purposes |
||||||
|
* (which is not needed often); most likely it is needed for use |
||||||
|
* with "mix-in annotations" (aka "annotation overrides"). |
||||||
|
* For most cases, however, default value of "true" is just fine |
||||||
|
* and should be omitted. |
||||||
|
*/ |
||||||
|
boolean value() default true; |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation that can be used to either suppress serialization of |
||||||
|
* properties (during serialization), or ignore processing of |
||||||
|
* JSON properties read (during deserialization). |
||||||
|
*<p> |
||||||
|
* Example: |
||||||
|
*<pre> |
||||||
|
* // to prevent specified fields from being serialized or deserialized
|
||||||
|
* // (i.e. not include in JSON output; or being set even if they were included)
|
||||||
|
* @JsonIgnoreProperties({ "internalId", "secretKey" }) |
||||||
|
* // To ignore any unknown properties in JSON input without exception:
|
||||||
|
* @JsonIgnoreProperties(ignoreUnknown=true) |
||||||
|
*</pre> |
||||||
|
*<p> |
||||||
|
* Starting with 2.0, this annotation can be applied both to classes and |
||||||
|
* to properties. If used for both, actual set will be union of all |
||||||
|
* ignorals: that is, you can only add properties to ignore, not remove |
||||||
|
* or override. So you can not remove properties to ignore using |
||||||
|
* per-property annotation. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, |
||||||
|
ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonIgnoreProperties |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Names of properties to ignore. |
||||||
|
*/ |
||||||
|
public String[] value() default { }; |
||||||
|
|
||||||
|
/** |
||||||
|
* Property that defines whether it is ok to just ignore any |
||||||
|
* unrecognized properties during deserialization. |
||||||
|
* If true, all properties that are unrecognized -- that is, |
||||||
|
* there are no setters or creators that accept them -- are |
||||||
|
* ignored without warnings (although handlers for unknown |
||||||
|
* properties, if any, will still be called) without |
||||||
|
* exception. |
||||||
|
*<p> |
||||||
|
* Does not have any effect on serialization. |
||||||
|
*/ |
||||||
|
public boolean ignoreUnknown() default false; |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that indicates that all properties of annotated |
||||||
|
* type are to be ignored during serialization and deserialization. |
||||||
|
*<p> |
||||||
|
* Note: annotation does have boolean 'value' property (which defaults |
||||||
|
* to 'true'), so that it is actually possible to override value |
||||||
|
* using mix-in annotations. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonIgnoreType |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Optional argument that defines whether this annotation is active |
||||||
|
* or not. The only use for value 'false' if for overriding purposes |
||||||
|
* (which is not needed often); most likely it is needed for use |
||||||
|
* with "mix-in annotations" ("annotation overrides"). |
||||||
|
* For most cases, however, default value of "true" is just fine |
||||||
|
* and should be omitted. |
||||||
|
*/ |
||||||
|
boolean value() default true; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used to indicate when value of the annotated property (when |
||||||
|
* used for a field, method or constructor parameter), or all |
||||||
|
* properties of the annotated class, is to be serialized. |
||||||
|
* Without annotation property values are always included, but by using |
||||||
|
* this annotation one can specify simple exclusion rules to reduce |
||||||
|
* amount of properties to write out. |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, |
||||||
|
ElementType.TYPE, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@com.fr.third.fasterxml.jackson.annotation.JacksonAnnotation |
||||||
|
public @interface JsonInclude |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Inclusion rule to use. |
||||||
|
*/ |
||||||
|
public Include value() default Include.ALWAYS; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Value enumerations needed |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Enumeration used with {@link JsonInclude} |
||||||
|
* to define which properties |
||||||
|
* of Java Beans are to be included in serialization. |
||||||
|
*<p> |
||||||
|
* Note: Jackson 1.x had similarly named ("Inclusion") enumeration included |
||||||
|
* in <code>JsonSerialize</code> annotation: it is not deprecated |
||||||
|
* and this value used instead. |
||||||
|
*/ |
||||||
|
public enum Include |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Value that indicates that property is to be always included, |
||||||
|
* independent of value of the property. |
||||||
|
*/ |
||||||
|
ALWAYS, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that only properties with non-null |
||||||
|
* values are to be included. |
||||||
|
*/ |
||||||
|
NON_NULL, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that only properties that have values |
||||||
|
* that differ from default settings (meaning values they have |
||||||
|
* when Bean is constructed with its no-arguments constructor) |
||||||
|
* are to be included. Value is generally not useful with |
||||||
|
* {@link java.util.Map}s, since they have no default values; |
||||||
|
* and if used, works same as {@link #ALWAYS}. |
||||||
|
*/ |
||||||
|
NON_DEFAULT, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that only properties that have values |
||||||
|
* that values that are null or what is considered empty are |
||||||
|
* not to be included. |
||||||
|
*<p> |
||||||
|
* Default emptiness is defined for following type: |
||||||
|
*<ul> |
||||||
|
* <li>For {@link java.util.Collection}s and {@link java.util.Map}s, |
||||||
|
* method <code>isEmpty()</code> is called; |
||||||
|
* </li> |
||||||
|
* <li>For Java arrays, empty arrays are ones with length of 0 |
||||||
|
* </li> |
||||||
|
* <li>For Java {@link java.lang.String}s, <code>length()</code> is called, |
||||||
|
* and return value of 0 indicates empty String (note that <code>String.isEmpty()</code> |
||||||
|
* was added in Java 1.6 and as such can not be used by Jackson |
||||||
|
* </li> |
||||||
|
* <ul> |
||||||
|
* and for other types, non-null values are to be included. |
||||||
|
*<p> |
||||||
|
* Note that this default handling can be overridden by custom |
||||||
|
* <code>JsonSerializer</code> implementation: if method <code>isEmpty()</code> |
||||||
|
* is overridden, it will be called to see if non-null values are |
||||||
|
* considered empty (null is always considered empty). |
||||||
|
*/ |
||||||
|
NON_EMPTY |
||||||
|
; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used to indicate that annotated property is part of |
||||||
|
* two-way linkage between fields; and that its role is "parent" (or "forward") link. |
||||||
|
* Value type (class) of property must have a single compatible property annotated with |
||||||
|
* {@link JsonBackReference}. Linkage is handled such that the property |
||||||
|
* annotated with this annotation is handled normally (serialized normally, no |
||||||
|
* special handling for deserialization); it is the matching back reference |
||||||
|
* that requires special handling |
||||||
|
*<p> |
||||||
|
* All references have logical name to allow handling multiple linkages; typical case |
||||||
|
* would be that where nodes have both parent/child and sibling linkages. If so, |
||||||
|
* pairs of references should be named differently. |
||||||
|
* It is an error for a class too have multiple managed references with same name, |
||||||
|
* even if types pointed are different. |
||||||
|
*<p> |
||||||
|
* Note: only methods and fields can be annotated with this annotation: constructor |
||||||
|
* arguments should NOT be annotated, as they can not be either managed or back |
||||||
|
* references. |
||||||
|
* |
||||||
|
* @author tatu |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonManagedReference |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Logical have for the reference property pair; used to link managed and |
||||||
|
* back references. Default name can be used if there is just single |
||||||
|
* reference pair (for example, node class that just has parent/child linkage, |
||||||
|
* consisting of one managed reference and matching back reference) |
||||||
|
*/ |
||||||
|
public String value() default "defaultReference"; |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that can be used to define a non-static |
||||||
|
* method as a "setter" or "getter" for a logical property |
||||||
|
* (depending on its signature), |
||||||
|
* or non-static object field to be used (serialized, deserialized) as |
||||||
|
* a logical property. |
||||||
|
*<p> |
||||||
|
* Default value ("") indicates that the field name is used |
||||||
|
* as the property name without any modifications, but it |
||||||
|
* can be specified to non-empty value to specify different |
||||||
|
* name. Property name refers to name used externally, as |
||||||
|
* the field name in JSON objects. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonProperty |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Special value that indicates that handlers should use the default |
||||||
|
* name (derived from method or field name) for property. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public final static String USE_DEFAULT_NAME = ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* Defines name of the logical property, i.e. JSON object field |
||||||
|
* name to use for the property. If value is empty String (which is the |
||||||
|
* default), will try to use name of the field that is annotated. |
||||||
|
* Note that there is |
||||||
|
* <b>no default name available for constructor arguments</b>, |
||||||
|
* meaning that |
||||||
|
* <b>Empty String is not a valid value for constructor arguments</b>. |
||||||
|
*/ |
||||||
|
String value() default USE_DEFAULT_NAME; |
||||||
|
|
||||||
|
/** |
||||||
|
* Property that indicates whether a value (which may be explicit |
||||||
|
* null) is expected for property during deserialization or not. |
||||||
|
* If expected, <code>BeanDeserialized</code> should indicate |
||||||
|
* this as a validity problem (usually by throwing an exception, |
||||||
|
* but this may be sent via problem handlers that can try to |
||||||
|
* rectify the problem, for example, by supplying a default |
||||||
|
* value). |
||||||
|
*<p> |
||||||
|
* Note that as of 2.0, this property is NOT used by |
||||||
|
* <code>BeanDeserializer</code>: support is expected to be |
||||||
|
* added for a later minor version. |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
boolean required() default false; |
||||||
|
|
||||||
|
/* NOTE: considering of adding ability to specify default |
||||||
|
* String value -- would work well for scalar types, most of |
||||||
|
* which can coerce from Strings. But won't add for 2.0 yet. |
||||||
|
*/ |
||||||
|
//String defaultValue() default "";
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotaion used to define a human readable description for a logical |
||||||
|
* property. |
||||||
|
* Currently used to populate the description field in generated JSON |
||||||
|
* Schemas. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonPropertyDescription |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Defines a human readable description of the logical property. |
||||||
|
* Currently used to populate the description field in generated JSON |
||||||
|
* Schemas. |
||||||
|
*/ |
||||||
|
String value() default ""; |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation that can be used to define ordering (possibly partial) to use |
||||||
|
* when serializing object properties. Properties included in annotation |
||||||
|
* declaration will be serialized first (in defined order), followed by |
||||||
|
* any properties not included in the definition. |
||||||
|
* Annotation definition will override any implicit orderings (such as |
||||||
|
* guarantee that Creator-properties are serialized before non-creator |
||||||
|
* properties) |
||||||
|
*<p> |
||||||
|
* Examples: |
||||||
|
*<pre> |
||||||
|
* // ensure that "id" and "name" are output before other properties
|
||||||
|
* <div>@</div>JsonPropertyOrder({ "id", "name" }) |
||||||
|
* // order any properties that don't have explicit setting using alphabetic order
|
||||||
|
* <div>@</div>JsonPropertyOrder(alphabetic=true) |
||||||
|
*</pre> |
||||||
|
*<p> |
||||||
|
* This annotation may or may not have effect on deserialization: for basic JSON |
||||||
|
* handling there is no effect, but for other supported data types (or structural |
||||||
|
* conventions) there may be. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonPropertyOrder |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Order in which properties of annotated object are to be serialized in. |
||||||
|
*/ |
||||||
|
public String[] value() default { }; |
||||||
|
|
||||||
|
/** |
||||||
|
* Property that defines what to do regarding ordering of properties |
||||||
|
* not explicitly included in annotation instance. If set to true, |
||||||
|
* they will be alphabetically ordered; if false, order is |
||||||
|
* undefined (default setting) |
||||||
|
*/ |
||||||
|
public boolean alphabetic() default false; |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that indicates that the annotated method |
||||||
|
* or field should be serialized by including literal String value |
||||||
|
* of the property as is, without quoting of characters. |
||||||
|
* This can be useful for injecting values already serialized in JSON or |
||||||
|
* passing javascript function definitions from server to a javascript client. |
||||||
|
*<p> |
||||||
|
* Warning: the resulting JSON stream may be invalid depending on your input value. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD }) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonRawValue |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Optional argument that defines whether this annotation is active |
||||||
|
* or not. The only use for value 'false' if for overriding purposes |
||||||
|
* (which is not needed often); most likely it is needed for use |
||||||
|
* with "mix-in annotations" (aka "annotation overrides"). |
||||||
|
* For most cases, however, default value of "true" is just fine |
||||||
|
* and should be omitted. |
||||||
|
*/ |
||||||
|
boolean value() default true; |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation similar to {@link javax.xml.bind.annotation.XmlRootElement}, |
||||||
|
* used to indicate name to use for root-level wrapping, if wrapping is |
||||||
|
* enabled. Annotation itself does not indicate that wrapping should |
||||||
|
* be used; but if it is, name used for serialization should be name |
||||||
|
* specified here, and deserializer will expect the name as well. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@com.fr.third.fasterxml.jackson.annotation.JacksonAnnotation |
||||||
|
public @interface JsonRootName |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Root name to use if root-level wrapping is enabled. |
||||||
|
*/ |
||||||
|
public String value(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that can be used to define a non-static, |
||||||
|
* single-argument method to be used as a "setter" for a logical property |
||||||
|
* as an alternative to recommended |
||||||
|
* {@link JsonProperty} annotation (which was introduced in version 1.1). |
||||||
|
*<p> |
||||||
|
* Setter means that when a property with matching name is encountered in |
||||||
|
* JSON content, this method will be used to set value of the property. |
||||||
|
*<p> |
||||||
|
* NOTE: this annotation was briefly deprecated for version 1.5; but has |
||||||
|
* since been un-deprecated to both allow for asymmetric naming (possibly |
||||||
|
* different name when reading and writing JSON), and more importantly to |
||||||
|
* allow multi-argument setter method in future. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonSetter |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Optional default argument that defines logical property this |
||||||
|
* method is used to modify ("set"); this is the property |
||||||
|
* name used in JSON content. |
||||||
|
*/ |
||||||
|
String value() default ""; |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used with {@link JsonTypeInfo} to indicate sub types of serializable |
||||||
|
* polymorphic types, and to associate logical names used within JSON content |
||||||
|
* (which is more portable than using physical Java class names). |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD, |
||||||
|
ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonSubTypes { |
||||||
|
/** |
||||||
|
* Subtypes of the annotated type (annotated class, or property value type |
||||||
|
* associated with the annotated method). These will be checked recursively |
||||||
|
* so that types can be defined by only including direct subtypes. |
||||||
|
*/ |
||||||
|
public Type[] value(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Definition of a subtype, along with optional name. If name is missing, class
|
||||||
|
* of the type will be checked for {@link JsonTypeName} annotation; and if that |
||||||
|
* is also missing or empty, a default |
||||||
|
* name will be constructed by type id mechanism. |
||||||
|
* Default name is usually based on class name. |
||||||
|
*/ |
||||||
|
public @interface Type { |
||||||
|
/** |
||||||
|
* Class of the subtype |
||||||
|
*/ |
||||||
|
public Class<?> value(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Logical type name used as the type identifier for the class
|
||||||
|
*/ |
||||||
|
public String name() default ""; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation that can be used on a property accessor |
||||||
|
* (field, getter or setter, constructor parameter) to indicate that |
||||||
|
* the property is to contain type id to use when including |
||||||
|
* polymorphic type information. |
||||||
|
* Annotation should <b>only be used</b> if the intent is to override |
||||||
|
* generation of standard type id: if so, value of the property will be |
||||||
|
* accessed during serialization and used as the type id. |
||||||
|
*<p> |
||||||
|
* On deserialization annotation has no effect, as visibility of type id |
||||||
|
* is governed by value of {@link JsonTypeInfo#visible}; properties with |
||||||
|
* this annotation get no special handling. |
||||||
|
*<p> |
||||||
|
* On serialization, this annotation will exclude property from being |
||||||
|
* serialized along other properties; instead, its value is serialized |
||||||
|
* as the type identifier. Since type identifier may be included in |
||||||
|
* various places, it may still appear like 'normal' property (when using |
||||||
|
* {@link JsonTypeInfo.As#PROPERTY}), but is more commonly embedded |
||||||
|
* in a different place, as per inclusion rules (see {@link JsonTypeInfo} |
||||||
|
* for details). |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonTypeId |
||||||
|
{ |
||||||
|
|
||||||
|
} |
@ -0,0 +1,277 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used for configuring details of if and how type information is |
||||||
|
* used with JSON serialization and deserialization, to preserve information |
||||||
|
* about actual class of Object instances. This is necessarily for polymorphic |
||||||
|
* types, and may also be needed to link abstract declared types and matching |
||||||
|
* concrete implementation. |
||||||
|
*<p> |
||||||
|
* Some examples of typical annotations: |
||||||
|
*<pre> |
||||||
|
* // Include Java class name ("com.myempl.ImplClass") as JSON property "class"
|
||||||
|
* @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class") |
||||||
|
* |
||||||
|
* // Include logical type name (defined in impl classes) as wrapper; 2 annotations
|
||||||
|
* @JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT) |
||||||
|
* @JsonSubTypes({com.myemp.Impl1.class, com.myempl.Impl2.class}) |
||||||
|
*</pre> |
||||||
|
* Alternatively you can also define fully customized type handling by using |
||||||
|
* <code>@JsonTypeResolver</code> annotation (from databind package). |
||||||
|
*<p> |
||||||
|
* This annotation can be used both for types (classes) and properties. |
||||||
|
* If both exist, annotation on property has precedence, as it is |
||||||
|
* considered more specific. |
||||||
|
*<p> |
||||||
|
* When used for properties (fields, methods), this annotation applies |
||||||
|
* to <b>values</b>: so when applied to structure types |
||||||
|
* (like {@link java.util.Collection}, {@link java.util.Map}, arrays), |
||||||
|
* will apply to contained values, not the container; |
||||||
|
* for non-structured types there is no difference. |
||||||
|
* This is identical to how JAXB handles type information |
||||||
|
* annotations; and is chosen since it is the dominant use case. |
||||||
|
* There is no per-property way to force type information to be included |
||||||
|
* for type of container (structured type); for container types one has |
||||||
|
* to use annotation for type declaration. |
||||||
|
*<p> |
||||||
|
* Note on visibility of type identifier: by default, deserialization |
||||||
|
* (use during reading of JSON) of type identifier |
||||||
|
* is completely handled by Jackson, and is <b>not passed to</b> |
||||||
|
* deserializers. However, if so desired, |
||||||
|
* it is possible to define property <code>visible = true</code> |
||||||
|
* in which case property will be passed as-is to deserializers |
||||||
|
* (and set via setter or field) on deserialization. |
||||||
|
*<p> |
||||||
|
* On serialization side, Jackson will generate type id by itself, |
||||||
|
* except if there is a property with name that matches |
||||||
|
* {@link #property()}, in which case value of that property is |
||||||
|
* used instead. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, |
||||||
|
ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonTypeInfo |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Value enumerations used for properties |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Definition of different type identifiers that can be included in JSON |
||||||
|
* during serialization, and used for deserialization. |
||||||
|
*/ |
||||||
|
public enum Id { |
||||||
|
/** |
||||||
|
* This means that no explicit type metadata is included, and typing is |
||||||
|
* purely done using contextual information possibly augmented with other |
||||||
|
* annotations. |
||||||
|
*/ |
||||||
|
NONE(null), |
||||||
|
|
||||||
|
/** |
||||||
|
* Means that fully-qualified Java class name is used as the type identifier. |
||||||
|
*/ |
||||||
|
CLASS("@class"), |
||||||
|
|
||||||
|
/** |
||||||
|
* Means that Java class name with minimal path is used as the type identifier. |
||||||
|
* Minimal means that only the class name, and that part of preceding Java |
||||||
|
* package name is included that is needed to construct fully-qualified name |
||||||
|
* given fully-qualified name of the declared supertype; additionally a single |
||||||
|
* leading dot ('.') must be used to indicate that partial class name is used. |
||||||
|
* For example, for supertype "com.foobar.Base", and concrete type |
||||||
|
* "com.foo.Impl", only ".Impl" would be included; and for "com.foo.impl.Impl2" |
||||||
|
* only ".impl.Impl2" would be included.<br /> |
||||||
|
* <b>NOTE</b>: leading dot ('.') MUST be used to denote partial (minimal) name; |
||||||
|
* if it is missing, value is assumed to be fully-qualified name. Fully-qualified |
||||||
|
* name is used in cases where subtypes are not in same package (or sub-package
|
||||||
|
* thereof) as base class. |
||||||
|
*<p> |
||||||
|
* If all related classes are in the same Java package, this option can reduce |
||||||
|
* amount of type information overhead, especially for small types. |
||||||
|
* However, please note that using this alternative is inherently risky since it |
||||||
|
* assumes that the |
||||||
|
* supertype can be reliably detected. Given that it is based on declared type |
||||||
|
* (since ultimate supertype, <code>java.lang.Object</code> would not be very |
||||||
|
* useful reference point), this may not always work as expected. |
||||||
|
*/ |
||||||
|
MINIMAL_CLASS("@c"), |
||||||
|
|
||||||
|
/** |
||||||
|
* Means that logical type name is used as type information; name will then need |
||||||
|
* to be separately resolved to actual concrete type (Class). |
||||||
|
*/ |
||||||
|
NAME("@type"), |
||||||
|
|
||||||
|
/** |
||||||
|
* Means that typing mechanism uses customized handling, with possibly |
||||||
|
* custom configuration. This means that semantics of other properties is |
||||||
|
* not defined by Jackson package, but by the custom implementation. |
||||||
|
*/ |
||||||
|
CUSTOM(null) |
||||||
|
; |
||||||
|
|
||||||
|
private final String _defaultPropertyName; |
||||||
|
|
||||||
|
private Id(String defProp) { |
||||||
|
_defaultPropertyName = defProp; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDefaultPropertyName() { return _defaultPropertyName; } |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Definition of standard type inclusion mechanisms for type metadata. |
||||||
|
* Used for standard metadata types, except for {@link Id#NONE}. |
||||||
|
* May or may not be used for custom types ({@link Id#CUSTOM}). |
||||||
|
*/ |
||||||
|
public enum As { |
||||||
|
/** |
||||||
|
* Inclusion mechanism that uses a single configurable property, included |
||||||
|
* along with actual data (POJO properties) as a separate meta-property. |
||||||
|
* <p> |
||||||
|
* Default choice for inclusion. |
||||||
|
*/ |
||||||
|
PROPERTY, |
||||||
|
|
||||||
|
/** |
||||||
|
* Inclusion mechanism that wraps typed JSON value (POJO |
||||||
|
* serialized as JSON) in |
||||||
|
* a JSON Object that has a single entry, |
||||||
|
* where field name is serialized type identifier, |
||||||
|
* and value is the actual JSON value. |
||||||
|
*<p> |
||||||
|
* Note: can only be used if type information can be serialized as |
||||||
|
* String. This is true for standard type metadata types, but not |
||||||
|
* necessarily for custom types. |
||||||
|
*/ |
||||||
|
WRAPPER_OBJECT, |
||||||
|
|
||||||
|
/** |
||||||
|
* Inclusion mechanism that wraps typed JSON value (POJO |
||||||
|
* serialized as JSON) in |
||||||
|
* a 2-element JSON array: first element is the serialized |
||||||
|
* type identifier, and second element the serialized POJO |
||||||
|
* as JSON Object. |
||||||
|
*/ |
||||||
|
WRAPPER_ARRAY, |
||||||
|
|
||||||
|
/** |
||||||
|
* Inclusion mechanism similar to <code>PROPERTY</code>, except that |
||||||
|
* property is included one-level higher in hierarchy, i.e. as sibling |
||||||
|
* property at same level as JSON Object to type. |
||||||
|
* Note that this choice <b>can only be used for properties</b>, not |
||||||
|
* for types (classes). Trying to use it for classes will result in |
||||||
|
* inclusion strategy of basic <code>PROPERTY</code> instead. |
||||||
|
*/ |
||||||
|
EXTERNAL_PROPERTY, |
||||||
|
|
||||||
|
/** |
||||||
|
* Inclusion mechanism similar to <code>PROPERTY</code> with respect |
||||||
|
* to deserialization; but one that is produced by a "regular" accessible |
||||||
|
* property during serialization. This means that <code>TypeSerializer</code> |
||||||
|
* will do nothing, and expect a property with defined name to be output |
||||||
|
* using some other mechanism (like default POJO property serialization, or |
||||||
|
* custom serializer). |
||||||
|
*<p> |
||||||
|
* Note that this behavior is quite similar to that of using {@link JsonTypeId}; |
||||||
|
* except that here <code>TypeSerializer</code> is basically suppressed; |
||||||
|
* whereas with {@link JsonTypeId}, output of regular property is suppressed. |
||||||
|
* This mostly matters with respect to output order; this choice is the only |
||||||
|
* way to ensure specific placement of type id during serialization. |
||||||
|
* |
||||||
|
* @since 2.3.0 |
||||||
|
*/ |
||||||
|
EXISTING_PROPERTY |
||||||
|
; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Annotation properties |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* What kind of type metadata is to be used for serializing and deserializing |
||||||
|
* type information for instances of annotated type (and its subtypes |
||||||
|
* unless overridden) |
||||||
|
*/ |
||||||
|
public Id use(); |
||||||
|
|
||||||
|
/** |
||||||
|
* What mechanism is used for including type metadata (if any; for |
||||||
|
* {@link Id#NONE} nothing is included). Default |
||||||
|
*<p> |
||||||
|
* Note that for type metadata type of {@link Id#CUSTOM}, |
||||||
|
* this setting may or may not have any effect. |
||||||
|
*/ |
||||||
|
public As include() default As.PROPERTY; |
||||||
|
|
||||||
|
/** |
||||||
|
* Property names used when type inclusion method ({@link As#PROPERTY}) is used |
||||||
|
* (or possibly when using type metadata of type {@link Id#CUSTOM}). |
||||||
|
* If POJO itself has a property with same name, value of property |
||||||
|
* will be set with type id metadata: if no such property exists, type id |
||||||
|
* is only used for determining actual type. |
||||||
|
*<p> |
||||||
|
* Default property name used if this property is not explicitly defined |
||||||
|
* (or is set to empty String) is based on |
||||||
|
* type metadata type ({@link #use}) used. |
||||||
|
*/ |
||||||
|
public String property() default ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* Optional property that can be used to specify default implementation |
||||||
|
* class to use if type identifier is either not present, or can not |
||||||
|
* be mapped to a registered type (which can occur for ids, but not when |
||||||
|
* specifying explicit class to use). |
||||||
|
*<p> |
||||||
|
* Note that while this property allows specification of the default |
||||||
|
* implementation to use, it does not help with structural issues that |
||||||
|
* may arise if type information is missing. This means that most often |
||||||
|
* this is used with type-name -based resolution, to cover cases |
||||||
|
* where new sub-types are added, but base type is not changed to |
||||||
|
* reference new sub-types. |
||||||
|
*<p> |
||||||
|
* There are certain special values that indicate alternate behavior: |
||||||
|
*<ul> |
||||||
|
* <li>{@link None} means "there is no default implementation" (in which |
||||||
|
* case an error results from unmappable type) |
||||||
|
* <li><code>com.fr.third.fasterxml.jackson.databind.annotation.NoClass</code> means that |
||||||
|
* objects with unmappable (or missing) type are to be mapped to null references. |
||||||
|
* </ul> |
||||||
|
*/ |
||||||
|
public Class<?> defaultImpl() default None.class; |
||||||
|
|
||||||
|
/** |
||||||
|
* Property that defines whether type identifier value will be passed |
||||||
|
* as part of JSON stream to deserializer (true), or handled and |
||||||
|
* removed by <code>TypeDeserializer</code> (false). |
||||||
|
*<p> |
||||||
|
* Default value is false, meaning that Jackson handles and removes |
||||||
|
* the type identifier from JSON content that is passed to |
||||||
|
* <code>JsonDeserializer</code>. |
||||||
|
* |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
public boolean visible() default false; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Helper classes |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* This marker class that is only to be used with <code>defaultImpl</code> |
||||||
|
* annotation property, to indicate that there is no default implementation |
||||||
|
* specified. |
||||||
|
*/ |
||||||
|
public abstract static class None { } |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used for binding logical name that the annotated class
|
||||||
|
* has. Used with {@link JsonTypeInfo} (and specifically its |
||||||
|
* {@link JsonTypeInfo#use} property) to establish relationship |
||||||
|
* between type names and types. |
||||||
|
* |
||||||
|
* @author tatu |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonTypeName { |
||||||
|
/** |
||||||
|
* Logical type name for annotated type. If missing (or defined as Empty String), |
||||||
|
* defaults to using non-qualified class name as the type. |
||||||
|
*/ |
||||||
|
public String value() default ""; |
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used to indicate that a property should be serialized |
||||||
|
* "unwrapped"; that is, if it would be serialized as JSON Object, its |
||||||
|
* properties are instead included as properties of its containing |
||||||
|
* Object. For example, consider case of POJO like: |
||||||
|
* |
||||||
|
*<pre> |
||||||
|
* public class Parent { |
||||||
|
* public int age; |
||||||
|
* public Name name; |
||||||
|
* } |
||||||
|
* public class Name { |
||||||
|
* public String first, last; |
||||||
|
* } |
||||||
|
*</pre> |
||||||
|
* which would normally be serialized as follows (assuming @JsonUnwrapped |
||||||
|
* had no effect): |
||||||
|
*<pre> |
||||||
|
* { |
||||||
|
* "age" : 18, |
||||||
|
* "name" : { |
||||||
|
* "first" : "Joey", |
||||||
|
* "last" : "Sixpack" |
||||||
|
* } |
||||||
|
* } |
||||||
|
*</pre> |
||||||
|
* can be changed to this: |
||||||
|
*<pre> |
||||||
|
* { |
||||||
|
* "age" : 18, |
||||||
|
* "first" : "Joey", |
||||||
|
* "last" : "Sixpack" |
||||||
|
* } |
||||||
|
*</pre> |
||||||
|
* by changing Parent class to: |
||||||
|
*<pre> |
||||||
|
* public class Parent { |
||||||
|
* public int age; |
||||||
|
* @JsonUnwrapped |
||||||
|
* public Name name; |
||||||
|
* } |
||||||
|
*</pre> |
||||||
|
* Annotation can only be added to properties, and not classes, as it is contextual. |
||||||
|
*<p> |
||||||
|
* Also note that annotation only applies if |
||||||
|
*<ul> |
||||||
|
* <li>Value is serialized as JSON Object (can not unwrap JSON arrays using this |
||||||
|
* mechanism) |
||||||
|
* </li> |
||||||
|
* <li>Serialization is done using <code>BeanSerializer</code>, not a custom serializer |
||||||
|
* </li> |
||||||
|
* <li>No type information is added; if type information needs to be added, structure can |
||||||
|
* not be altered regardless of inclusion strategy; so annotation is basically ignored. |
||||||
|
* </li> |
||||||
|
* </ul> |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonUnwrapped |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Property that is usually only used when overriding (masking) annotations, |
||||||
|
* using mix-in annotations. Otherwise default value of 'true' is fine, and |
||||||
|
* value need not be explicitly included. |
||||||
|
*/ |
||||||
|
boolean enabled() default true; |
||||||
|
|
||||||
|
/** |
||||||
|
* Optional property that can be used to add prefix String to use in front |
||||||
|
* of names of properties that are unwrapped: this can be done for example to prevent |
||||||
|
* name collisions. |
||||||
|
*/ |
||||||
|
String prefix() default ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* Optional property that can be used to add suffix String to append at the end |
||||||
|
* of names of properties that are unwrapped: this can be done for example to prevent |
||||||
|
* name collisions. |
||||||
|
*/ |
||||||
|
String suffix() default ""; |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker annotation similar to |
||||||
|
* {@link javax.xml.bind.annotation.XmlValue} |
||||||
|
* that indicates that results of the annotated "getter" method |
||||||
|
* (which means signature must be that of getters; non-void return |
||||||
|
* type, no args) is to be used as the single value to serialize |
||||||
|
* for the instance. Usually value will be of a simple scalar type |
||||||
|
* (String or Number), but it can be any serializable type (Collection, |
||||||
|
* Map or Bean). |
||||||
|
*<p> |
||||||
|
* At most one method of a <code>Class</code> can be annotated with this annotation; |
||||||
|
* if more than one is found, an exception may be thrown. |
||||||
|
* Also, if method signature is not compatible with Getters, an exception |
||||||
|
* may be thrown (whether exception is thrown or not is an implementation detail (due |
||||||
|
* to filtering during introspection, some annotations may be skipped) |
||||||
|
* and applications should not rely on specific behavior). |
||||||
|
*<p> |
||||||
|
* A typical usage is that of annotating <code>toString()</code> |
||||||
|
* method so that returned String value is used as the JSON serialization; |
||||||
|
* and if deserialization is needed, there is matching constructor |
||||||
|
* or factory method annotated with {@link JsonCreator} annotation. |
||||||
|
*<p> |
||||||
|
* Boolean argument is only used so that sub-classes can "disable" |
||||||
|
* annotation if necessary. |
||||||
|
*<p> |
||||||
|
* NOTE: when use for Java <code>enum</code>s, one additional feature is |
||||||
|
* that value returned by annotated method is also considered to be the |
||||||
|
* value to deserialize from, not just JSON String to serialize as. |
||||||
|
* This is possible since set of Enum values is constant and it is possible |
||||||
|
* to define mapping, but can not be done in general for POJO types; as such, |
||||||
|
* this is not used for POJO deserialization. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonValue |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Optional argument that defines whether this annotation is active |
||||||
|
* or not. The only use for value 'false' if for overriding purposes. |
||||||
|
* Overriding may be necessary when used |
||||||
|
* with "mix-in annotations" (aka "annotation overrides"). |
||||||
|
* For most cases, however, default value of "true" is just fine |
||||||
|
* and should be omitted. |
||||||
|
*/ |
||||||
|
boolean value() default true; |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.annotation.JacksonAnnotation; |
||||||
|
|
||||||
|
/** |
||||||
|
* Annotation used for indicating view(s) that the property |
||||||
|
* that is defined by method or field annotated is part of. |
||||||
|
*<p> |
||||||
|
* An example annotation would be: |
||||||
|
*<pre> |
||||||
|
* @JsonView(BasicView.class) |
||||||
|
*</pre> |
||||||
|
* which would specify that property annotated would be included |
||||||
|
* when processing (serializing, deserializing) View identified |
||||||
|
* by <code>BasicView.class</code> (or its sub-class). |
||||||
|
* If multiple View class identifiers are included, property will |
||||||
|
* be part of all of them. |
||||||
|
*/ |
||||||
|
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@JacksonAnnotation |
||||||
|
public @interface JsonView { |
||||||
|
/** |
||||||
|
* View or views that annotated element is part of. Views are identified |
||||||
|
* by classes, and use expected class inheritance relationship: child |
||||||
|
* views contain all elements parent views have, for example. |
||||||
|
*/ |
||||||
|
public Class<?>[] value() default { }; |
||||||
|
} |
@ -0,0 +1,145 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
/** |
||||||
|
* Definition of API used for constructing Object Identifiers |
||||||
|
* (as annotated using {@link JsonIdentityInfo}). |
||||||
|
* Also defines factory methods used for creating instances |
||||||
|
* for serialization, deserialization. |
||||||
|
* |
||||||
|
* @param <T> Type of Object Identifiers produced. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public abstract class ObjectIdGenerator<T> |
||||||
|
implements java.io.Serializable |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Accessors |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public abstract Class<?> getScope(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called to check whether this generator instance can |
||||||
|
* be used for Object Ids of specific generator type and |
||||||
|
* scope; determination is based by passing a configured |
||||||
|
* "blueprint" (prototype) instance; from which the actual |
||||||
|
* instances are created (using {@link #newForSerialization}). |
||||||
|
* |
||||||
|
* @return True if this instance can be used as-is; false if not |
||||||
|
*/ |
||||||
|
public abstract boolean canUseFor(ObjectIdGenerator<?> gen); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Factory methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Factory method to create a blueprint instance for specified |
||||||
|
* scope. Generators that do not use scope may return 'this'. |
||||||
|
*/ |
||||||
|
public abstract ObjectIdGenerator<T> forScope(Class<?> scope); |
||||||
|
|
||||||
|
/** |
||||||
|
* Factory method called to create a new instance to use for |
||||||
|
* serialization: needed since generators may have state |
||||||
|
* (next id to produce). |
||||||
|
*<p> |
||||||
|
* Note that actual type of 'context' is |
||||||
|
* <code>com.fr.third.fasterxml.jackson.databind.SerializerProvider</code>, |
||||||
|
* but can not be declared here as type itself (as well as call |
||||||
|
* to this object) comes from databind package. |
||||||
|
* |
||||||
|
* @param context Serialization context object used (of type |
||||||
|
* <code>com.fr.third.fasterxml.jackson.databind.SerializerProvider</code>; |
||||||
|
* may be needed by more complex generators to access contextual |
||||||
|
* information such as configuration. |
||||||
|
*/ |
||||||
|
public abstract ObjectIdGenerator<T> newForSerialization(Object context); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for constructing key to use for ObjectId-to-POJO maps. |
||||||
|
*/ |
||||||
|
public abstract IdKey key(Object key); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Methods for serialization |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method used for generating a new Object Identifier to serialize |
||||||
|
* for given POJO. |
||||||
|
* |
||||||
|
* @param forPojo POJO for which identifier is needed |
||||||
|
* |
||||||
|
* @return Object Identifier to use. |
||||||
|
*/ |
||||||
|
public abstract T generateId(Object forPojo); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Helper classes |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Simple key class that can be used as a key for |
||||||
|
* ObjectId-to-POJO mappings, when multiple ObjectId types |
||||||
|
* and scopes are used. |
||||||
|
*/ |
||||||
|
public final static class IdKey |
||||||
|
implements java.io.Serializable |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Type of {@link ObjectIdGenerator} used for generating Object Id |
||||||
|
*/ |
||||||
|
private final Class<?> type; |
||||||
|
|
||||||
|
/** |
||||||
|
* Scope of the Object Id (may be null, to denote global) |
||||||
|
*/ |
||||||
|
private final Class<?> scope; |
||||||
|
|
||||||
|
/** |
||||||
|
* Object for which Object Id was generated: can NOT be null. |
||||||
|
*/ |
||||||
|
private final Object key; |
||||||
|
|
||||||
|
/** |
||||||
|
* Hash code |
||||||
|
*/ |
||||||
|
private final int hashCode; |
||||||
|
|
||||||
|
public IdKey(Class<?> type, Class<?> scope, Object key) { |
||||||
|
this.type = type; |
||||||
|
this.scope = scope; |
||||||
|
this.key = key; |
||||||
|
|
||||||
|
int h = key.hashCode() + type.getName().hashCode(); |
||||||
|
if (scope != null) { |
||||||
|
h ^= scope.getName().hashCode(); |
||||||
|
} |
||||||
|
hashCode = h; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { return hashCode; } |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) |
||||||
|
{ |
||||||
|
if (o == this) return true; |
||||||
|
if (o == null) return false; |
||||||
|
if (o.getClass() != getClass()) return false; |
||||||
|
IdKey other = (IdKey) o; |
||||||
|
return (other.key.equals(key)) && (other.type == type) && (other.scope == scope); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,164 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
import java.util.UUID; |
||||||
|
|
||||||
|
/** |
||||||
|
* Container class for standard {@link ObjectIdGenerator} implementations. |
||||||
|
*/ |
||||||
|
public class ObjectIdGenerators |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Shared base class for concrete implementations |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper class for implementations contained. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
private abstract static class Base<T> extends ObjectIdGenerator<T> |
||||||
|
{ |
||||||
|
protected final Class<?> _scope; |
||||||
|
|
||||||
|
protected Base(Class<?> scope) { |
||||||
|
_scope = scope; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final Class<?> getScope() { |
||||||
|
return _scope; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean canUseFor(ObjectIdGenerator<?> gen) { |
||||||
|
return (gen.getClass() == getClass()) && (gen.getScope() == _scope); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract T generateId(Object forPojo); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Implementation classes |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract marker class used to allow explicitly specifying |
||||||
|
* that no generator is used; which also implies that no |
||||||
|
* Object Id is to be included or used. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("serial") |
||||||
|
public abstract static class None extends ObjectIdGenerator<Object> { } |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract place-holder class which is used to denote case |
||||||
|
* where Object Identifier to use comes from a POJO property |
||||||
|
* (getter method or field). If so, value is written directly |
||||||
|
* during serialization, and used as-is during deserialization. |
||||||
|
*<p> |
||||||
|
* Actual implementation class is part of <code>databind</code> |
||||||
|
* package. |
||||||
|
*/ |
||||||
|
public abstract static class PropertyGenerator extends Base<Object> { |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
protected PropertyGenerator(Class<?> scope) { super(scope); } |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Simple sequence-number based generator, which uses basic Java |
||||||
|
* <code>int</code>s (starting with value 1) as Object Identifiers. |
||||||
|
*/ |
||||||
|
public final static class IntSequenceGenerator extends Base<Integer> |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
protected transient int _nextValue; |
||||||
|
|
||||||
|
public IntSequenceGenerator() { this(Object.class, -1); } |
||||||
|
public IntSequenceGenerator(Class<?> scope, int fv) { |
||||||
|
super(scope); |
||||||
|
_nextValue = fv; |
||||||
|
} |
||||||
|
|
||||||
|
protected int initialValue() { return 1; } |
||||||
|
|
||||||
|
@Override |
||||||
|
public ObjectIdGenerator<Integer> forScope(Class<?> scope) { |
||||||
|
return (_scope == scope) ? this : new IntSequenceGenerator(scope, _nextValue); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ObjectIdGenerator<Integer> newForSerialization(Object context) { |
||||||
|
return new IntSequenceGenerator(_scope, initialValue()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public IdKey key(Object key) { |
||||||
|
return new IdKey(getClass(), _scope, key); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Integer generateId(Object forPojo) { |
||||||
|
int id = _nextValue; |
||||||
|
++_nextValue; |
||||||
|
return id; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implementation that just uses {@link java.util.UUID}s as reliably |
||||||
|
* unique identifiers: downside is that resulting String is |
||||||
|
* 36 characters long. |
||||||
|
*<p> |
||||||
|
* One difference to other generators is that scope is always |
||||||
|
* set as <code>Object.class</code> (regardless of arguments): this |
||||||
|
* because UUIDs are globally unique, and scope has no meaning. |
||||||
|
*/ |
||||||
|
public final static class UUIDGenerator extends Base<UUID> |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
public UUIDGenerator() { this(Object.class); } |
||||||
|
private UUIDGenerator(Class<?> scope) { |
||||||
|
super(Object.class); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Can just return base instance since this is essentially scopeless |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public ObjectIdGenerator<UUID> forScope(Class<?> scope) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Can just return base instance since this is essentially scopeless |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public ObjectIdGenerator<UUID> newForSerialization(Object context) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UUID generateId(Object forPojo) { |
||||||
|
return UUID.randomUUID(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public IdKey key(Object key) { |
||||||
|
return new IdKey(getClass(), null, key); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Since UUIDs are always unique, let's fully ignore scope definition |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean canUseFor(ObjectIdGenerator<?> gen) { |
||||||
|
return (gen.getClass() == getClass()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
||||||
|
|
||||||
|
/** |
||||||
|
* Enumeration used to define kinds of elements (called "property accessors") |
||||||
|
* that annotations like {@link JsonAutoDetect} apply to. |
||||||
|
*<p> |
||||||
|
* In addition to method types (GETTER/IS_GETTER, SETTER, CREATOR) and the |
||||||
|
* field type (FIELD), 2 pseudo-types |
||||||
|
* are defined for convenience: <code>ALWAYS</code> and <code>NONE</code>. These |
||||||
|
* can be used to indicate, all or none of available method types (respectively), |
||||||
|
* for use by annotations that takes <code>JsonMethod</code> argument. |
||||||
|
*/ |
||||||
|
public enum PropertyAccessor |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Getters are methods used to get a POJO field value for serialization, |
||||||
|
* or, under certain conditions also for de-serialization. Latter |
||||||
|
* can be used for effectively setting Collection or Map values |
||||||
|
* in absence of setters, iff returned value is not a copy but |
||||||
|
* actual value of the logical property. |
||||||
|
*<p> |
||||||
|
* Since version 1.3, this does <b>NOT</b> include "is getters" (methods |
||||||
|
* that return boolean and named 'isXxx' for property 'xxx'); instead, |
||||||
|
* {@link #IS_GETTER} is used}. |
||||||
|
*/ |
||||||
|
GETTER, |
||||||
|
|
||||||
|
/** |
||||||
|
* Setters are methods used to set a POJO value for deserialization. |
||||||
|
*/ |
||||||
|
SETTER, |
||||||
|
|
||||||
|
/** |
||||||
|
* Creators are constructors and (static) factory methods used to |
||||||
|
* construct POJO instances for deserialization |
||||||
|
*/ |
||||||
|
CREATOR, |
||||||
|
|
||||||
|
/** |
||||||
|
* Field refers to fields of regular Java objects. Although |
||||||
|
* they are not really methods, addition of optional field-discovery |
||||||
|
* in version 1.1 meant that there was need to enable/disable |
||||||
|
* their auto-detection, and this is the place to add it in. |
||||||
|
*/ |
||||||
|
FIELD, |
||||||
|
|
||||||
|
/** |
||||||
|
* "Is getters" are getter-like methods that are named "isXxx" |
||||||
|
* (instead of "getXxx" for getters) and return boolean value |
||||||
|
* (either primitive, or {@link java.lang.Boolean}). |
||||||
|
* |
||||||
|
*/ |
||||||
|
IS_GETTER, |
||||||
|
|
||||||
|
/** |
||||||
|
* This pseudo-type indicates that none of accessors if affected. |
||||||
|
*/ |
||||||
|
NONE, |
||||||
|
|
||||||
|
/** |
||||||
|
* This pseudo-type indicates that all accessors are affected. |
||||||
|
*/ |
||||||
|
ALL |
||||||
|
; |
||||||
|
|
||||||
|
private PropertyAccessor() { } |
||||||
|
|
||||||
|
public boolean creatorEnabled() { |
||||||
|
return (this == CREATOR) || (this == ALL); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean getterEnabled() { |
||||||
|
return (this == GETTER) || (this == ALL); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isGetterEnabled() { |
||||||
|
return (this == IS_GETTER) || (this == ALL); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean setterEnabled() { |
||||||
|
return (this == SETTER) || (this == ALL); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean fieldEnabled() { |
||||||
|
return (this == FIELD) || (this == ALL); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
/** |
||||||
|
* Public core annotations, most of which are used to configure how |
||||||
|
* Data Mapping/Binding works. Annotations in this package can only |
||||||
|
* have dependencies to non-annotation classes in Core package; |
||||||
|
* annotations that have dependencies to Mapper classes are included |
||||||
|
* in Mapper module (under <code>org.codehaus.jackson.map.annotate</code>). |
||||||
|
* Also contains parameter types (mostly enums) needed by annotations. |
||||||
|
*<p> |
||||||
|
* Note that prior versions (1.x) contained these annotations within |
||||||
|
* 'core' jar, as part of Streaming API. |
||||||
|
*/ |
||||||
|
package com.fr.third.fasterxml.jackson.annotation; |
@ -0,0 +1,593 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.util.ByteArrayBuilder; |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract base class used to define specific details of which |
||||||
|
* variant of Base64 encoding/decoding is to be used. Although there is |
||||||
|
* somewhat standard basic version (so-called "MIME Base64"), other variants |
||||||
|
* exists, see <a href="http://en.wikipedia.org/wiki/Base64">Base64 Wikipedia entry</a> for details. |
||||||
|
* |
||||||
|
* @author Tatu Saloranta |
||||||
|
*/ |
||||||
|
public final class Base64Variant |
||||||
|
implements java.io.Serializable |
||||||
|
{ |
||||||
|
private final static int INT_SPACE = 0x20; |
||||||
|
|
||||||
|
// We'll only serialize name
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Placeholder used by "no padding" variant, to be used when a character |
||||||
|
* value is needed. |
||||||
|
*/ |
||||||
|
final static char PADDING_CHAR_NONE = '\0'; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker used to denote ascii characters that do not correspond |
||||||
|
* to a 6-bit value (in this variant), and is not used as a padding |
||||||
|
* character. |
||||||
|
*/ |
||||||
|
public final static int BASE64_VALUE_INVALID = -1; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker used to denote ascii character (in decoding table) that |
||||||
|
* is the padding character using this variant (if any). |
||||||
|
*/ |
||||||
|
public final static int BASE64_VALUE_PADDING = -2; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Encoding/decoding tables |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Decoding table used for base 64 decoding. |
||||||
|
*/ |
||||||
|
private final transient int[] _asciiToBase64 = new int[128]; |
||||||
|
|
||||||
|
/** |
||||||
|
* Encoding table used for base 64 decoding when output is done |
||||||
|
* as characters. |
||||||
|
*/ |
||||||
|
private final transient char[] _base64ToAsciiC = new char[64]; |
||||||
|
|
||||||
|
/** |
||||||
|
* Alternative encoding table used for base 64 decoding when output is done |
||||||
|
* as ascii bytes. |
||||||
|
*/ |
||||||
|
private final transient byte[] _base64ToAsciiB = new byte[64]; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Other configuration |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Symbolic name of variant; used for diagnostics/debugging. |
||||||
|
*<p> |
||||||
|
* Note that this is the only non-transient field; used when reading |
||||||
|
* back from serialized state |
||||||
|
*/ |
||||||
|
protected final String _name; |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether this variant uses padding or not. |
||||||
|
*/ |
||||||
|
protected final transient boolean _usesPadding; |
||||||
|
|
||||||
|
/** |
||||||
|
* Characted used for padding, if any ({@link #PADDING_CHAR_NONE} if not). |
||||||
|
*/ |
||||||
|
protected final transient char _paddingChar; |
||||||
|
|
||||||
|
/** |
||||||
|
* Maximum number of encoded base64 characters to output during encoding |
||||||
|
* before adding a linefeed, if line length is to be limited |
||||||
|
* ({@link java.lang.Integer#MAX_VALUE} if not limited). |
||||||
|
*<p> |
||||||
|
* Note: for some output modes (when writing attributes) linefeeds may |
||||||
|
* need to be avoided, and this value ignored. |
||||||
|
*/ |
||||||
|
protected final transient int _maxLineLength; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public Base64Variant(String name, String base64Alphabet, boolean usesPadding, char paddingChar, int maxLineLength) |
||||||
|
{ |
||||||
|
_name = name; |
||||||
|
_usesPadding = usesPadding; |
||||||
|
_paddingChar = paddingChar; |
||||||
|
_maxLineLength = maxLineLength; |
||||||
|
|
||||||
|
// Ok and then we need to create codec tables.
|
||||||
|
|
||||||
|
// First the main encoding table:
|
||||||
|
int alphaLen = base64Alphabet.length(); |
||||||
|
if (alphaLen != 64) { |
||||||
|
throw new IllegalArgumentException("Base64Alphabet length must be exactly 64 (was "+alphaLen+")"); |
||||||
|
} |
||||||
|
|
||||||
|
// And then secondary encoding table and decoding table:
|
||||||
|
base64Alphabet.getChars(0, alphaLen, _base64ToAsciiC, 0); |
||||||
|
Arrays.fill(_asciiToBase64, BASE64_VALUE_INVALID); |
||||||
|
for (int i = 0; i < alphaLen; ++i) { |
||||||
|
char alpha = _base64ToAsciiC[i]; |
||||||
|
_base64ToAsciiB[i] = (byte) alpha; |
||||||
|
_asciiToBase64[alpha] = i; |
||||||
|
} |
||||||
|
|
||||||
|
// Plus if we use padding, add that in too
|
||||||
|
if (usesPadding) { |
||||||
|
_asciiToBase64[(int) paddingChar] = BASE64_VALUE_PADDING; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* "Copy constructor" that can be used when the base alphabet is identical |
||||||
|
* to one used by another variant except for the maximum line length |
||||||
|
* (and obviously, name). |
||||||
|
*/ |
||||||
|
public Base64Variant(Base64Variant base, String name, int maxLineLength) |
||||||
|
{ |
||||||
|
this(base, name, base._usesPadding, base._paddingChar, maxLineLength); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* "Copy constructor" that can be used when the base alphabet is identical |
||||||
|
* to one used by another variant, but other details (padding, maximum |
||||||
|
* line length) differ |
||||||
|
*/ |
||||||
|
public Base64Variant(Base64Variant base, String name, boolean usesPadding, char paddingChar, int maxLineLength) |
||||||
|
{ |
||||||
|
_name = name; |
||||||
|
byte[] srcB = base._base64ToAsciiB; |
||||||
|
System.arraycopy(srcB, 0, this._base64ToAsciiB, 0, srcB.length); |
||||||
|
char[] srcC = base._base64ToAsciiC; |
||||||
|
System.arraycopy(srcC, 0, this._base64ToAsciiC, 0, srcC.length); |
||||||
|
int[] srcV = base._asciiToBase64; |
||||||
|
System.arraycopy(srcV, 0, this._asciiToBase64, 0, srcV.length); |
||||||
|
|
||||||
|
_usesPadding = usesPadding; |
||||||
|
_paddingChar = paddingChar; |
||||||
|
_maxLineLength = maxLineLength; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Serializable overrides |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method used to "demote" deserialized instances back to |
||||||
|
* canonical ones |
||||||
|
*/ |
||||||
|
protected Object readResolve() { |
||||||
|
return Base64Variants.valueOf(_name); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public accessors |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public String getName() { return _name; } |
||||||
|
|
||||||
|
public boolean usesPadding() { return _usesPadding; } |
||||||
|
public boolean usesPaddingChar(char c) { return c == _paddingChar; } |
||||||
|
public boolean usesPaddingChar(int ch) { return ch == (int) _paddingChar; } |
||||||
|
public char getPaddingChar() { return _paddingChar; } |
||||||
|
public byte getPaddingByte() { return (byte)_paddingChar; } |
||||||
|
|
||||||
|
public int getMaxLineLength() { return _maxLineLength; } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Decoding support |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @return 6-bit decoded value, if valid character; |
||||||
|
*/ |
||||||
|
public int decodeBase64Char(char c) |
||||||
|
{ |
||||||
|
int ch = (int) c; |
||||||
|
return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; |
||||||
|
} |
||||||
|
|
||||||
|
public int decodeBase64Char(int ch) |
||||||
|
{ |
||||||
|
return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; |
||||||
|
} |
||||||
|
|
||||||
|
public int decodeBase64Byte(byte b) |
||||||
|
{ |
||||||
|
int ch = (int) b; |
||||||
|
return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Encoding support |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public char encodeBase64BitsAsChar(int value) |
||||||
|
{ |
||||||
|
/* Let's assume caller has done necessary checks; this |
||||||
|
* method must be fast and inlinable |
||||||
|
*/ |
||||||
|
return _base64ToAsciiC[value]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that encodes given right-aligned (LSB) 24-bit value |
||||||
|
* into 4 base64 characters, stored in given result buffer. |
||||||
|
*/ |
||||||
|
public int encodeBase64Chunk(int b24, char[] buffer, int ptr) |
||||||
|
{ |
||||||
|
buffer[ptr++] = _base64ToAsciiC[(b24 >> 18) & 0x3F]; |
||||||
|
buffer[ptr++] = _base64ToAsciiC[(b24 >> 12) & 0x3F]; |
||||||
|
buffer[ptr++] = _base64ToAsciiC[(b24 >> 6) & 0x3F]; |
||||||
|
buffer[ptr++] = _base64ToAsciiC[b24 & 0x3F]; |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
public void encodeBase64Chunk(StringBuilder sb, int b24) |
||||||
|
{ |
||||||
|
sb.append(_base64ToAsciiC[(b24 >> 18) & 0x3F]); |
||||||
|
sb.append(_base64ToAsciiC[(b24 >> 12) & 0x3F]); |
||||||
|
sb.append(_base64ToAsciiC[(b24 >> 6) & 0x3F]); |
||||||
|
sb.append(_base64ToAsciiC[b24 & 0x3F]); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that outputs partial chunk (which only encodes one |
||||||
|
* or two bytes of data). Data given is still aligned same as if |
||||||
|
* it as full data; that is, missing data is at the "right end" |
||||||
|
* (LSB) of int. |
||||||
|
* |
||||||
|
* @param outputBytes Number of encoded bytes included (either 1 or 2) |
||||||
|
*/ |
||||||
|
public int encodeBase64Partial(int bits, int outputBytes, char[] buffer, int outPtr) |
||||||
|
{ |
||||||
|
buffer[outPtr++] = _base64ToAsciiC[(bits >> 18) & 0x3F]; |
||||||
|
buffer[outPtr++] = _base64ToAsciiC[(bits >> 12) & 0x3F]; |
||||||
|
if (_usesPadding) { |
||||||
|
buffer[outPtr++] = (outputBytes == 2) ? |
||||||
|
_base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar; |
||||||
|
buffer[outPtr++] = _paddingChar; |
||||||
|
} else { |
||||||
|
if (outputBytes == 2) { |
||||||
|
buffer[outPtr++] = _base64ToAsciiC[(bits >> 6) & 0x3F]; |
||||||
|
} |
||||||
|
} |
||||||
|
return outPtr; |
||||||
|
} |
||||||
|
|
||||||
|
public void encodeBase64Partial(StringBuilder sb, int bits, int outputBytes) |
||||||
|
{ |
||||||
|
sb.append(_base64ToAsciiC[(bits >> 18) & 0x3F]); |
||||||
|
sb.append(_base64ToAsciiC[(bits >> 12) & 0x3F]); |
||||||
|
if (_usesPadding) { |
||||||
|
sb.append((outputBytes == 2) ? |
||||||
|
_base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar); |
||||||
|
sb.append(_paddingChar); |
||||||
|
} else { |
||||||
|
if (outputBytes == 2) { |
||||||
|
sb.append(_base64ToAsciiC[(bits >> 6) & 0x3F]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public byte encodeBase64BitsAsByte(int value) |
||||||
|
{ |
||||||
|
// As with above, assuming it is 6-bit value
|
||||||
|
return _base64ToAsciiB[value]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that encodes given right-aligned (LSB) 24-bit value |
||||||
|
* into 4 base64 bytes (ascii), stored in given result buffer. |
||||||
|
*/ |
||||||
|
public int encodeBase64Chunk(int b24, byte[] buffer, int ptr) |
||||||
|
{ |
||||||
|
buffer[ptr++] = _base64ToAsciiB[(b24 >> 18) & 0x3F]; |
||||||
|
buffer[ptr++] = _base64ToAsciiB[(b24 >> 12) & 0x3F]; |
||||||
|
buffer[ptr++] = _base64ToAsciiB[(b24 >> 6) & 0x3F]; |
||||||
|
buffer[ptr++] = _base64ToAsciiB[b24 & 0x3F]; |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that outputs partial chunk (which only encodes one |
||||||
|
* or two bytes of data). Data given is still aligned same as if |
||||||
|
* it as full data; that is, missing data is at the "right end" |
||||||
|
* (LSB) of int. |
||||||
|
* |
||||||
|
* @param outputBytes Number of encoded bytes included (either 1 or 2) |
||||||
|
*/ |
||||||
|
public int encodeBase64Partial(int bits, int outputBytes, byte[] buffer, int outPtr) |
||||||
|
{ |
||||||
|
buffer[outPtr++] = _base64ToAsciiB[(bits >> 18) & 0x3F]; |
||||||
|
buffer[outPtr++] = _base64ToAsciiB[(bits >> 12) & 0x3F]; |
||||||
|
if (_usesPadding) { |
||||||
|
byte pb = (byte) _paddingChar; |
||||||
|
buffer[outPtr++] = (outputBytes == 2) ? |
||||||
|
_base64ToAsciiB[(bits >> 6) & 0x3F] : pb; |
||||||
|
buffer[outPtr++] = pb; |
||||||
|
} else { |
||||||
|
if (outputBytes == 2) { |
||||||
|
buffer[outPtr++] = _base64ToAsciiB[(bits >> 6) & 0x3F]; |
||||||
|
} |
||||||
|
} |
||||||
|
return outPtr; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Convenience conversion methods for String to/from bytes |
||||||
|
/* use case. |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method for converting given byte array as base64 encoded |
||||||
|
* String using this variant's settings. |
||||||
|
* Resulting value is "raw", that is, not enclosed in double-quotes. |
||||||
|
* |
||||||
|
* @param input Byte array to encode |
||||||
|
*/ |
||||||
|
public String encode(byte[] input) |
||||||
|
{ |
||||||
|
return encode(input, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method for converting given byte array as base64 encoded String |
||||||
|
* using this variant's settings, |
||||||
|
* optionally enclosed in double-quotes. |
||||||
|
* |
||||||
|
* @param input Byte array to encode |
||||||
|
* @param addQuotes Whether to surround resulting value in double quotes or not |
||||||
|
*/ |
||||||
|
public String encode(byte[] input, boolean addQuotes) |
||||||
|
{ |
||||||
|
int inputEnd = input.length; |
||||||
|
StringBuilder sb; |
||||||
|
{ |
||||||
|
// let's approximate... 33% overhead, ~= 3/8 (0.375)
|
||||||
|
int outputLen = inputEnd + (inputEnd >> 2) + (inputEnd >> 3); |
||||||
|
sb = new StringBuilder(outputLen); |
||||||
|
} |
||||||
|
if (addQuotes) { |
||||||
|
sb.append('"'); |
||||||
|
} |
||||||
|
|
||||||
|
int chunksBeforeLF = getMaxLineLength() >> 2; |
||||||
|
|
||||||
|
// Ok, first we loop through all full triplets of data:
|
||||||
|
int inputPtr = 0; |
||||||
|
int safeInputEnd = inputEnd-3; // to get only full triplets
|
||||||
|
|
||||||
|
while (inputPtr <= safeInputEnd) { |
||||||
|
// First, mash 3 bytes into lsb of 32-bit int
|
||||||
|
int b24 = ((int) input[inputPtr++]) << 8; |
||||||
|
b24 |= ((int) input[inputPtr++]) & 0xFF; |
||||||
|
b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); |
||||||
|
encodeBase64Chunk(sb, b24); |
||||||
|
if (--chunksBeforeLF <= 0) { |
||||||
|
// note: must quote in JSON value, so not really useful...
|
||||||
|
sb.append('\\'); |
||||||
|
sb.append('n'); |
||||||
|
chunksBeforeLF = getMaxLineLength() >> 2; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// And then we may have 1 or 2 leftover bytes to encode
|
||||||
|
int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
|
||||||
|
if (inputLeft > 0) { // yes, but do we have room for output?
|
||||||
|
int b24 = ((int) input[inputPtr++]) << 16; |
||||||
|
if (inputLeft == 2) { |
||||||
|
b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; |
||||||
|
} |
||||||
|
encodeBase64Partial(sb, b24, inputLeft); |
||||||
|
} |
||||||
|
|
||||||
|
if (addQuotes) { |
||||||
|
sb.append('"'); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method for decoding contents of a Base64-encoded String, |
||||||
|
* using this variant's settings. |
||||||
|
* |
||||||
|
* @param input |
||||||
|
* |
||||||
|
* @since 2.2.3 |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if input is not valid base64 encoded data |
||||||
|
*/ |
||||||
|
@SuppressWarnings("resource") |
||||||
|
public byte[] decode(String input) throws IllegalArgumentException |
||||||
|
{ |
||||||
|
ByteArrayBuilder b = new ByteArrayBuilder(); |
||||||
|
decode(input, b); |
||||||
|
return b.toByteArray(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method for decoding contents of a Base64-encoded String, |
||||||
|
* using this variant's settings |
||||||
|
* and appending decoded binary data using provided {@link ByteArrayBuilder}. |
||||||
|
*<p> |
||||||
|
* NOTE: builder will NOT be reset before decoding (nor cleared afterwards); |
||||||
|
* assumption is that caller will ensure it is given in proper state, and |
||||||
|
* used as appropriate afterwards. |
||||||
|
* |
||||||
|
* @since 2.2.3 |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if input is not valid base64 encoded data |
||||||
|
*/ |
||||||
|
public void decode(String str, ByteArrayBuilder builder) throws IllegalArgumentException |
||||||
|
{ |
||||||
|
int ptr = 0; |
||||||
|
int len = str.length(); |
||||||
|
|
||||||
|
main_loop: |
||||||
|
while (ptr < len) { |
||||||
|
// first, we'll skip preceding white space, if any
|
||||||
|
char ch; |
||||||
|
do { |
||||||
|
ch = str.charAt(ptr++); |
||||||
|
if (ptr >= len) { |
||||||
|
break main_loop; |
||||||
|
} |
||||||
|
} while (ch <= INT_SPACE); |
||||||
|
int bits = decodeBase64Char(ch); |
||||||
|
if (bits < 0) { |
||||||
|
_reportInvalidBase64(ch, 0, null); |
||||||
|
} |
||||||
|
int decodedData = bits; |
||||||
|
// then second base64 char; can't get padding yet, nor ws
|
||||||
|
if (ptr >= len) { |
||||||
|
_reportBase64EOF(); |
||||||
|
} |
||||||
|
ch = str.charAt(ptr++); |
||||||
|
bits = decodeBase64Char(ch); |
||||||
|
if (bits < 0) { |
||||||
|
_reportInvalidBase64(ch, 1, null); |
||||||
|
} |
||||||
|
decodedData = (decodedData << 6) | bits; |
||||||
|
// third base64 char; can be padding, but not ws
|
||||||
|
if (ptr >= len) { |
||||||
|
// but as per [JACKSON-631] can be end-of-input, iff not using padding
|
||||||
|
if (!usesPadding()) { |
||||||
|
decodedData >>= 4; |
||||||
|
builder.append(decodedData); |
||||||
|
break; |
||||||
|
} |
||||||
|
_reportBase64EOF(); |
||||||
|
} |
||||||
|
ch = str.charAt(ptr++); |
||||||
|
bits = decodeBase64Char(ch); |
||||||
|
|
||||||
|
// First branch: can get padding (-> 1 byte)
|
||||||
|
if (bits < 0) { |
||||||
|
if (bits != Base64Variant.BASE64_VALUE_PADDING) { |
||||||
|
_reportInvalidBase64(ch, 2, null); |
||||||
|
} |
||||||
|
// Ok, must get padding
|
||||||
|
if (ptr >= len) { |
||||||
|
_reportBase64EOF(); |
||||||
|
} |
||||||
|
ch = str.charAt(ptr++); |
||||||
|
if (!usesPaddingChar(ch)) { |
||||||
|
_reportInvalidBase64(ch, 3, "expected padding character '"+getPaddingChar()+"'"); |
||||||
|
} |
||||||
|
// Got 12 bits, only need 8, need to shift
|
||||||
|
decodedData >>= 4; |
||||||
|
builder.append(decodedData); |
||||||
|
continue; |
||||||
|
} |
||||||
|
// Nope, 2 or 3 bytes
|
||||||
|
decodedData = (decodedData << 6) | bits; |
||||||
|
// fourth and last base64 char; can be padding, but not ws
|
||||||
|
if (ptr >= len) { |
||||||
|
// but as per [JACKSON-631] can be end-of-input, iff not using padding
|
||||||
|
if (!usesPadding()) { |
||||||
|
decodedData >>= 2; |
||||||
|
builder.appendTwoBytes(decodedData); |
||||||
|
break; |
||||||
|
} |
||||||
|
_reportBase64EOF(); |
||||||
|
} |
||||||
|
ch = str.charAt(ptr++); |
||||||
|
bits = decodeBase64Char(ch); |
||||||
|
if (bits < 0) { |
||||||
|
if (bits != Base64Variant.BASE64_VALUE_PADDING) { |
||||||
|
_reportInvalidBase64(ch, 3, null); |
||||||
|
} |
||||||
|
decodedData >>= 2; |
||||||
|
builder.appendTwoBytes(decodedData); |
||||||
|
} else { |
||||||
|
// otherwise, our triple is now complete
|
||||||
|
decodedData = (decodedData << 6) | bits; |
||||||
|
builder.appendThreeBytes(decodedData); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Overridden standard methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { return _name; } |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
// identity comparison should be dine
|
||||||
|
return (o == this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
return _name.hashCode(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal helper methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @param bindex Relative index within base64 character unit; between 0 |
||||||
|
* and 3 (as unit has exactly 4 characters) |
||||||
|
*/ |
||||||
|
protected void _reportInvalidBase64(char ch, int bindex, String msg) |
||||||
|
throws IllegalArgumentException |
||||||
|
{ |
||||||
|
String base; |
||||||
|
if (ch <= INT_SPACE) { |
||||||
|
base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units"; |
||||||
|
} else if (usesPaddingChar(ch)) { |
||||||
|
base = "Unexpected padding character ('"+getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character"; |
||||||
|
} else if (!Character.isDefined(ch) || Character.isISOControl(ch)) { |
||||||
|
// Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
|
||||||
|
base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content"; |
||||||
|
} else { |
||||||
|
base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; |
||||||
|
} |
||||||
|
if (msg != null) { |
||||||
|
base = base + ": " + msg; |
||||||
|
} |
||||||
|
throw new IllegalArgumentException(base); |
||||||
|
} |
||||||
|
|
||||||
|
protected void _reportBase64EOF() throws IllegalArgumentException { |
||||||
|
throw new IllegalArgumentException("Unexpected end-of-String in base64 content"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,111 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Container for commonly used Base64 variants: |
||||||
|
*<ul> |
||||||
|
* <li> {@link #MIME} |
||||||
|
* <li> {@link #MIME_NO_LINEFEEDS} |
||||||
|
* <li> {@link #PEM} |
||||||
|
* <li> {@link #MODIFIED_FOR_URL} |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* @author Tatu Saloranta |
||||||
|
*/ |
||||||
|
public final class Base64Variants |
||||||
|
{ |
||||||
|
final static String STD_BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
||||||
|
|
||||||
|
/** |
||||||
|
* This variant is what most people would think of "the standard" |
||||||
|
* Base64 encoding. |
||||||
|
*<p> |
||||||
|
* See <a href="">wikipedia Base64 entry</a> for details. |
||||||
|
*<p> |
||||||
|
* Note that although this can be thought of as the standard variant, |
||||||
|
* it is <b>not</b> the default for Jackson: no-linefeeds alternative |
||||||
|
* is because of JSON requirement of escaping all linefeeds. |
||||||
|
*/ |
||||||
|
public final static Base64Variant MIME; |
||||||
|
static { |
||||||
|
MIME = new Base64Variant("MIME", STD_BASE64_ALPHABET, true, '=', 76); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Slightly non-standard modification of {@link #MIME} which does not |
||||||
|
* use linefeeds (max line length set to infinite). Useful when linefeeds |
||||||
|
* wouldn't work well (possibly in attributes), or for minor space savings |
||||||
|
* (save 1 linefeed per 76 data chars, ie. ~1.4% savings). |
||||||
|
*/ |
||||||
|
public final static Base64Variant MIME_NO_LINEFEEDS; |
||||||
|
static { |
||||||
|
MIME_NO_LINEFEEDS = new Base64Variant(MIME, "MIME-NO-LINEFEEDS", Integer.MAX_VALUE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This variant is the one that predates {@link #MIME}: it is otherwise |
||||||
|
* identical, except that it mandates shorter line length. |
||||||
|
*/ |
||||||
|
public final static Base64Variant PEM = new Base64Variant(MIME, "PEM", true, '=', 64); |
||||||
|
|
||||||
|
/** |
||||||
|
* This non-standard variant is usually used when encoded data needs to be |
||||||
|
* passed via URLs (such as part of GET request). It differs from the |
||||||
|
* base {@link #MIME} variant in multiple ways. |
||||||
|
* First, no padding is used: this also means that it generally can not |
||||||
|
* be written in multiple separate but adjacent chunks (which would not |
||||||
|
* be the usual use case in any case). Also, no linefeeds are used (max |
||||||
|
* line length set to infinite). And finally, two characters (plus and |
||||||
|
* slash) that would need quoting in URLs are replaced with more |
||||||
|
* optimal alternatives (hyphen and underscore, respectively). |
||||||
|
*/ |
||||||
|
public final static Base64Variant MODIFIED_FOR_URL; |
||||||
|
static { |
||||||
|
StringBuffer sb = new StringBuffer(STD_BASE64_ALPHABET); |
||||||
|
// Replace plus with hyphen, slash with underscore (and no padding)
|
||||||
|
sb.setCharAt(sb.indexOf("+"), '-'); |
||||||
|
sb.setCharAt(sb.indexOf("/"), '_'); |
||||||
|
/* And finally, let's not split lines either, wouldn't work too |
||||||
|
* well with URLs |
||||||
|
*/ |
||||||
|
MODIFIED_FOR_URL = new Base64Variant("MODIFIED-FOR-URL", sb.toString(), false, Base64Variant.PADDING_CHAR_NONE, Integer.MAX_VALUE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method used to get the default variant ("MIME_NO_LINEFEEDS") for cases |
||||||
|
* where caller does not explicitly specify the variant. |
||||||
|
* We will prefer no-linefeed version because linefeeds in JSON values |
||||||
|
* must be escaped, making linefeed-containing variants sub-optimal. |
||||||
|
*/ |
||||||
|
public static Base64Variant getDefaultVariant() { |
||||||
|
return MIME_NO_LINEFEEDS; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public static Base64Variant valueOf(String name) throws IllegalArgumentException |
||||||
|
{ |
||||||
|
if (MIME._name.equals(name)) { |
||||||
|
return MIME; |
||||||
|
} |
||||||
|
if (MIME_NO_LINEFEEDS._name.equals(name)) { |
||||||
|
return MIME_NO_LINEFEEDS; |
||||||
|
} |
||||||
|
if (PEM._name.equals(name)) { |
||||||
|
return PEM; |
||||||
|
} |
||||||
|
if (MODIFIED_FOR_URL._name.equals(name)) { |
||||||
|
return MODIFIED_FOR_URL; |
||||||
|
} |
||||||
|
if (name == null) { |
||||||
|
name = "<null>"; |
||||||
|
} else { |
||||||
|
name = "'"+name+"'"; |
||||||
|
} |
||||||
|
throw new IllegalArgumentException("No Base64Variant with name "+name); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Simple tag interface used to mark schema objects that are used by some |
||||||
|
* {@link JsonParser} and {@link JsonGenerator} implementations to further |
||||||
|
* specify structure of expected format. |
||||||
|
* Basic JSON-based parsers and generators do not use schemas, but some data |
||||||
|
* formats (like many binary data formats like Thrift, protobuf) mandate |
||||||
|
* use of schemas. |
||||||
|
*<p> |
||||||
|
* Since there is little commonality between schemas for different data formats, |
||||||
|
* this interface does not define much meaningful functionality for accessing |
||||||
|
* schema details; rather, specific parser and generator implementations need |
||||||
|
* to cast to schema implementations they use. This marker interface is mostly |
||||||
|
* used for tagging "some kind of schema" -- instead of passing opaque |
||||||
|
* {@link java.lang.Object} -- for documentation purposes. |
||||||
|
*/ |
||||||
|
public interface FormatSchema |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Method that can be used to get an identifier that can be used for diagnostics |
||||||
|
* purposes, to indicate what kind of data format this schema is used for: typically |
||||||
|
* it is a short name of format itself, but it can also contain additional information |
||||||
|
* in cases where data format supports multiple types of schemas. |
||||||
|
*/ |
||||||
|
String getSchemaType(); |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Enumeration that defines legal encodings that can be used |
||||||
|
* for JSON content, based on list of allowed encodings from |
||||||
|
* <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a>. |
||||||
|
*<p> |
||||||
|
* Note: if application want to explicitly disregard Encoding |
||||||
|
* limitations (to read in JSON encoded using an encoding not |
||||||
|
* listed as allowed), they can use {@link java.io.Reader} / |
||||||
|
* {@link java.io.Writer} instances as input |
||||||
|
*/ |
||||||
|
public enum JsonEncoding { |
||||||
|
UTF8("UTF-8", false, 8), // N/A for big-endian, really
|
||||||
|
UTF16_BE("UTF-16BE", true, 16), |
||||||
|
UTF16_LE("UTF-16LE", false, 16), |
||||||
|
UTF32_BE("UTF-32BE", true, 32), |
||||||
|
UTF32_LE("UTF-32LE", false, 32) |
||||||
|
; |
||||||
|
|
||||||
|
protected final String _javaName; |
||||||
|
|
||||||
|
protected final boolean _bigEndian; |
||||||
|
|
||||||
|
protected final int _bits; |
||||||
|
|
||||||
|
JsonEncoding(String javaName, boolean bigEndian, int bits) |
||||||
|
{ |
||||||
|
_javaName = javaName; |
||||||
|
_bigEndian = bigEndian; |
||||||
|
_bits = bits; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing encoding name that JDK will support. |
||||||
|
* |
||||||
|
* @return Matching encoding name that JDK will support. |
||||||
|
*/ |
||||||
|
public String getJavaName() { return _javaName; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether encoding is big-endian (if encoding supports such |
||||||
|
* notion). If no such distinction is made (as is the case for |
||||||
|
* {@link #UTF8}), return value is undefined. |
||||||
|
* |
||||||
|
* @return True for big-endian encodings; false for little-endian |
||||||
|
* (or if not applicable) |
||||||
|
*/ |
||||||
|
public boolean isBigEndian() { return _bigEndian; } |
||||||
|
|
||||||
|
public int bits() { return _bits; } |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception type for exceptions during JSON writing, such as trying |
||||||
|
* to output content in wrong context (non-matching end-array or end-object, |
||||||
|
* for example). |
||||||
|
*/ |
||||||
|
public class JsonGenerationException |
||||||
|
extends JsonProcessingException |
||||||
|
{ |
||||||
|
private final static long serialVersionUID = 123; // Stupid eclipse...
|
||||||
|
|
||||||
|
public JsonGenerationException(Throwable rootCause) |
||||||
|
{ |
||||||
|
super(rootCause); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonGenerationException(String msg) |
||||||
|
{ |
||||||
|
super(msg, (JsonLocation)null); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonGenerationException(String msg, Throwable rootCause) |
||||||
|
{ |
||||||
|
super(msg, (JsonLocation)null, rootCause); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,139 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Object that encapsulates Location information used for reporting |
||||||
|
* parsing (or potentially generation) errors, as well as current location |
||||||
|
* within input streams. |
||||||
|
*/ |
||||||
|
public class JsonLocation |
||||||
|
implements java.io.Serializable // as per [JACKSON-168]
|
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Shared immutable "N/A location" that can be returned to indicate |
||||||
|
* that no location information is available |
||||||
|
*/ |
||||||
|
public final static JsonLocation NA = new JsonLocation("N/A", -1L, -1L, -1, -1); |
||||||
|
|
||||||
|
final long _totalBytes; |
||||||
|
final long _totalChars; |
||||||
|
|
||||||
|
final int _lineNr; |
||||||
|
final int _columnNr; |
||||||
|
|
||||||
|
/** |
||||||
|
* Displayable description for input source: file path, URL. |
||||||
|
*<p> |
||||||
|
* NOTE: <code>transient</code> since 2.2 so that Location itself is Serializable. |
||||||
|
*/ |
||||||
|
final transient Object _sourceRef; |
||||||
|
|
||||||
|
public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr) |
||||||
|
{ |
||||||
|
/* Unfortunately, none of legal encodings are straight single-byte |
||||||
|
* encodings. Could determine offset for UTF-16/UTF-32, but the |
||||||
|
* most important one is UTF-8... |
||||||
|
* so for now, we'll just not report any real byte count |
||||||
|
*/ |
||||||
|
this(srcRef, -1L, totalChars, lineNr, colNr); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonLocation(Object sourceRef, long totalBytes, long totalChars, |
||||||
|
int lineNr, int columnNr) |
||||||
|
{ |
||||||
|
_sourceRef = sourceRef; |
||||||
|
_totalBytes = totalBytes; |
||||||
|
_totalChars = totalChars; |
||||||
|
_lineNr = lineNr; |
||||||
|
_columnNr = columnNr; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to the original resource being read, if one available. |
||||||
|
* For example, when a parser has been constructed by passing |
||||||
|
* a {@link java.io.File} instance, this method would return |
||||||
|
* that File. Will return null if no such reference is available, |
||||||
|
* for example when {@link java.io.InputStream} was used to |
||||||
|
* construct the parser instance. |
||||||
|
*/ |
||||||
|
public Object getSourceRef() { return _sourceRef; } |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Line number of the location (1-based) |
||||||
|
*/ |
||||||
|
public int getLineNr() { return _lineNr; } |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Column number of the location (1-based) |
||||||
|
*/ |
||||||
|
public int getColumnNr() { return _columnNr; } |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Character offset within underlying stream, reader or writer, |
||||||
|
* if available; -1 if not. |
||||||
|
*/ |
||||||
|
public long getCharOffset() { return _totalChars; } |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Byte offset within underlying stream, reader or writer, |
||||||
|
* if available; -1 if not. |
||||||
|
*/ |
||||||
|
public long getByteOffset() |
||||||
|
{ |
||||||
|
return _totalBytes; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() |
||||||
|
{ |
||||||
|
StringBuilder sb = new StringBuilder(80); |
||||||
|
sb.append("[Source: "); |
||||||
|
if (_sourceRef == null) { |
||||||
|
sb.append("UNKNOWN"); |
||||||
|
} else { |
||||||
|
sb.append(_sourceRef.toString()); |
||||||
|
} |
||||||
|
sb.append("; line: "); |
||||||
|
sb.append(_lineNr); |
||||||
|
sb.append(", column: "); |
||||||
|
sb.append(_columnNr); |
||||||
|
sb.append(']'); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() |
||||||
|
{ |
||||||
|
int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode(); |
||||||
|
hash ^= _lineNr; |
||||||
|
hash += _columnNr; |
||||||
|
hash ^= (int) _totalChars; |
||||||
|
hash += (int) _totalBytes; |
||||||
|
return hash; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object other) |
||||||
|
{ |
||||||
|
if (other == this) return true; |
||||||
|
if (other == null) return false; |
||||||
|
if (!(other instanceof JsonLocation)) return false; |
||||||
|
JsonLocation otherLoc = (JsonLocation) other; |
||||||
|
|
||||||
|
if (_sourceRef == null) { |
||||||
|
if (otherLoc._sourceRef != null) return false; |
||||||
|
} else if (!_sourceRef.equals(otherLoc._sourceRef)) return false; |
||||||
|
|
||||||
|
return (_lineNr == otherLoc._lineNr) |
||||||
|
&& (_columnNr == otherLoc._columnNr) |
||||||
|
&& (_totalChars == otherLoc._totalChars) |
||||||
|
&& (getByteOffset() == otherLoc.getByteOffset()) |
||||||
|
; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Exception type for parsing problems, used when non-well-formed content |
||||||
|
* (content that does not conform to JSON syntax as per specification) |
||||||
|
* is encountered. |
||||||
|
*/ |
||||||
|
public class JsonParseException |
||||||
|
extends JsonProcessingException |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
public JsonParseException(String msg, JsonLocation loc) |
||||||
|
{ |
||||||
|
super(msg, loc); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonParseException(String msg, JsonLocation loc, Throwable root) |
||||||
|
{ |
||||||
|
super(msg, loc, root); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,277 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.io.NumberInput; |
||||||
|
|
||||||
|
/** |
||||||
|
* Implementation of |
||||||
|
* <a href="http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-03">JSON Pointer</a> |
||||||
|
* specification. |
||||||
|
* Pointer instances can be used to locate logical JSON nodes for things like |
||||||
|
* tree traversal (see {@link TreeNode#at}). |
||||||
|
* It may be used in future for filtering of streaming JSON content |
||||||
|
* as well (not implemented yet for 2.3). |
||||||
|
*<p> |
||||||
|
* Instances are fully immutable and can be shared, cached. |
||||||
|
* |
||||||
|
* @author Tatu Saloranta |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
public class JsonPointer |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Marker instance used to represent segment that matches current |
||||||
|
* node or position. |
||||||
|
*/ |
||||||
|
protected final static JsonPointer EMPTY = new JsonPointer(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to rest of the pointer beyond currently matching |
||||||
|
* segment (if any); null if this pointer refers to a matching |
||||||
|
* segment. |
||||||
|
*/ |
||||||
|
protected final JsonPointer _nextSegment; |
||||||
|
|
||||||
|
/** |
||||||
|
* We will retain representation of the pointer, as a String, |
||||||
|
* so that {@link #toString} should be as efficient as possible. |
||||||
|
*/ |
||||||
|
protected final String _asString; |
||||||
|
|
||||||
|
protected final String _matchingPropertyName; |
||||||
|
|
||||||
|
protected final int _matchingElementIndex; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Cosntruction |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor used for creating "empty" instance, used to represent |
||||||
|
* state that matches current node. |
||||||
|
*/ |
||||||
|
protected JsonPointer() { |
||||||
|
_nextSegment = null; |
||||||
|
_matchingPropertyName = ""; |
||||||
|
_matchingElementIndex = -1; |
||||||
|
_asString = ""; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor used for creating non-empty Segments |
||||||
|
*/ |
||||||
|
protected JsonPointer(String fullString, String segment, JsonPointer next) { |
||||||
|
_asString = fullString; |
||||||
|
_nextSegment = next; |
||||||
|
// Ok; may always be a property
|
||||||
|
_matchingPropertyName = segment; |
||||||
|
_matchingElementIndex = _parseIndex(segment); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Factory methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Factory method that parses given input and construct matching pointer |
||||||
|
* instance, if it represents a valid JSON Pointer: if not, a |
||||||
|
* {@link IllegalArgumentException} is thrown. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException Thrown if the input does not present a valid JSON Pointer |
||||||
|
* expression: currently the only such expression is one that does NOT start with |
||||||
|
* a slash ('/'). |
||||||
|
*/ |
||||||
|
public static JsonPointer compile(String input) throws IllegalArgumentException |
||||||
|
{ |
||||||
|
// First quick checks for well-known 'empty' pointer
|
||||||
|
if ((input == null) || input.length() == 0) { |
||||||
|
return EMPTY; |
||||||
|
} |
||||||
|
// And then quick validity check:
|
||||||
|
if (input.charAt(0) != '/') { |
||||||
|
throw new IllegalArgumentException("Invalid input: JSON Pointer expression must start with '/': "+"\""+input+"\""); |
||||||
|
} |
||||||
|
return _parseTail(input); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Alias for {@link #compile}; added to make instances automatically |
||||||
|
* deserializable by Jackson databind. |
||||||
|
*/ |
||||||
|
public static JsonPointer valueOf(String input) { return compile(input); } |
||||||
|
|
||||||
|
/* Factory method that composes a pointer instance, given a set |
||||||
|
* of 'raw' segments: raw meaning that no processing will be done, |
||||||
|
* no escaping may is present. |
||||||
|
* |
||||||
|
* @param segments |
||||||
|
* |
||||||
|
* @return Constructed path instance |
||||||
|
*/ |
||||||
|
/* TODO! |
||||||
|
public static JsonPointer fromSegment(String... segments) |
||||||
|
{ |
||||||
|
if (segments.length == 0) { |
||||||
|
return EMPTY; |
||||||
|
} |
||||||
|
JsonPointer prev = null; |
||||||
|
|
||||||
|
for (String segment : segments) { |
||||||
|
JsonPointer next = new JsonPointer() |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public boolean matches() { return _nextSegment == null; } |
||||||
|
public String getMatchingProperty() { return _matchingPropertyName; } |
||||||
|
public int getMatchingIndex() { return _matchingElementIndex; } |
||||||
|
public boolean mayMatchProperty() { return _matchingPropertyName != null; } |
||||||
|
public boolean mayMatchElement() { return _matchingElementIndex >= 0; } |
||||||
|
|
||||||
|
public JsonPointer matchProperty(String name) { |
||||||
|
if (_nextSegment == null || !_matchingPropertyName.equals(name)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return _nextSegment; |
||||||
|
} |
||||||
|
|
||||||
|
public JsonPointer matchElement (int index) { |
||||||
|
if ((index != _matchingElementIndex) || (index < 0)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return _nextSegment; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for getting a "sub-pointer", instance where current segment |
||||||
|
* has been removed and pointer includes rest of segments; |
||||||
|
*/ |
||||||
|
public JsonPointer tail() { |
||||||
|
return _nextSegment; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Standard method overrides |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override public String toString() { return _asString; } |
||||||
|
@Override public int hashCode() { return _asString.hashCode(); } |
||||||
|
|
||||||
|
@Override public boolean equals(Object o) { |
||||||
|
if (o == this) return true; |
||||||
|
if (o == null) return false; |
||||||
|
if (!(o instanceof JsonPointer)) return false; |
||||||
|
return _asString.equals(((JsonPointer) o)._asString); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
private final static int _parseIndex(String str) { |
||||||
|
final int len = str.length(); |
||||||
|
// [core#133]: beware of super long indexes; assume we never
|
||||||
|
// have arrays over 2 billion entries so ints are fine.
|
||||||
|
if (len == 0 || len > 10) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
// [core#176]: no leading zeroes allowed
|
||||||
|
char c = str.charAt(0); |
||||||
|
if (c <= '0') { |
||||||
|
return (len == 1 && c == '0') ? 0 : -1; |
||||||
|
} |
||||||
|
if (c > '9') { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
for (int i = 1; i < len; ++i) { |
||||||
|
c = str.charAt(i); |
||||||
|
if (c > '9' || c < '0') { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
if (len == 10) { |
||||||
|
long l = NumberInput.parseLong(str); |
||||||
|
if (l > Integer.MAX_VALUE) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
return NumberInput.parseInt(str); |
||||||
|
} |
||||||
|
|
||||||
|
protected static JsonPointer _parseTail(String input) { |
||||||
|
final int end = input.length(); |
||||||
|
|
||||||
|
// first char is the contextual slash, skip
|
||||||
|
for (int i = 1; i < end; ) { |
||||||
|
char c = input.charAt(i); |
||||||
|
if (c == '/') { // common case, got a segment
|
||||||
|
return new JsonPointer(input, input.substring(1, i), |
||||||
|
_parseTail(input.substring(i))); |
||||||
|
} |
||||||
|
++i; |
||||||
|
// quoting is different; offline this case
|
||||||
|
if (c == '~' && i < end) { // possibly, quote
|
||||||
|
return _parseQuotedTail(input, i); |
||||||
|
} |
||||||
|
// otherwise, loop on
|
||||||
|
} |
||||||
|
// end of the road, no escapes
|
||||||
|
return new JsonPointer(input, input.substring(1), EMPTY); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called to parse tail of pointer path, when a potentially |
||||||
|
* escaped character has been seen. |
||||||
|
* |
||||||
|
* @param input Full input for the tail being parsed |
||||||
|
* @param i Offset to character after tilde |
||||||
|
*/ |
||||||
|
protected static JsonPointer _parseQuotedTail(String input, int i) { |
||||||
|
final int end = input.length(); |
||||||
|
StringBuilder sb = new StringBuilder(Math.max(16, end)); |
||||||
|
if (i > 2) { |
||||||
|
sb.append(input, 1, i-1); |
||||||
|
} |
||||||
|
_appendEscape(sb, input.charAt(i++)); |
||||||
|
while (i < end) { |
||||||
|
char c = input.charAt(i); |
||||||
|
if (c == '/') { // end is nigh!
|
||||||
|
return new JsonPointer(input, sb.toString(), |
||||||
|
_parseTail(input.substring(i))); // need to push back slash
|
||||||
|
} |
||||||
|
++i; |
||||||
|
if (c == '~' && i < end) { |
||||||
|
_appendEscape(sb, input.charAt(i++)); |
||||||
|
continue; |
||||||
|
} |
||||||
|
sb.append(c); |
||||||
|
} |
||||||
|
// end of the road, last segment
|
||||||
|
return new JsonPointer(input, sb.toString(), EMPTY); |
||||||
|
} |
||||||
|
|
||||||
|
private static void _appendEscape(StringBuilder sb, char c) { |
||||||
|
if (c == '0') { |
||||||
|
c = '~'; |
||||||
|
} else if (c == '1') { |
||||||
|
c = '/'; |
||||||
|
} else { |
||||||
|
sb.append('~'); |
||||||
|
} |
||||||
|
sb.append(c); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,130 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Intermediate base class for all problems encountered when |
||||||
|
* processing (parsing, generating) JSON content |
||||||
|
* that are not pure I/O problems. |
||||||
|
* Regular {@link java.io.IOException}s will be passed through as is. |
||||||
|
* Sub-class of {@link java.io.IOException} for convenience. |
||||||
|
*/ |
||||||
|
public class JsonProcessingException |
||||||
|
extends java.io.IOException |
||||||
|
{ |
||||||
|
final static long serialVersionUID = 123; // Stupid eclipse...
|
||||||
|
|
||||||
|
protected JsonLocation _location; |
||||||
|
|
||||||
|
protected JsonProcessingException(String msg, JsonLocation loc, Throwable rootCause) |
||||||
|
{ |
||||||
|
/* Argh. IOException(Throwable,String) is only available starting |
||||||
|
* with JDK 1.6... |
||||||
|
*/ |
||||||
|
super(msg); |
||||||
|
if (rootCause != null) { |
||||||
|
initCause(rootCause); |
||||||
|
} |
||||||
|
_location = loc; |
||||||
|
} |
||||||
|
|
||||||
|
protected JsonProcessingException(String msg) |
||||||
|
{ |
||||||
|
super(msg); |
||||||
|
} |
||||||
|
|
||||||
|
protected JsonProcessingException(String msg, JsonLocation loc) |
||||||
|
{ |
||||||
|
this(msg, loc, null); |
||||||
|
} |
||||||
|
|
||||||
|
protected JsonProcessingException(String msg, Throwable rootCause) |
||||||
|
{ |
||||||
|
this(msg, null, rootCause); |
||||||
|
} |
||||||
|
|
||||||
|
protected JsonProcessingException(Throwable rootCause) |
||||||
|
{ |
||||||
|
this(null, null, rootCause); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonLocation getLocation() { |
||||||
|
return _location; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Extended API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that allows accessing the original "message" argument, |
||||||
|
* without additional decorations (like location information) |
||||||
|
* that overridden {@link #getMessage} adds. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public String getOriginalMessage() |
||||||
|
{ |
||||||
|
return super.getMessage(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Methods for sub-classes to use, override |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor that sub-classes can override to append additional |
||||||
|
* information right after the main message, but before |
||||||
|
* source location information. |
||||||
|
*/ |
||||||
|
protected String getMessageSuffix() { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Overrides of standard methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Default method overridden so that we can add location information |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String getMessage() |
||||||
|
{ |
||||||
|
String msg = super.getMessage(); |
||||||
|
if (msg == null) { |
||||||
|
msg = "N/A"; |
||||||
|
} |
||||||
|
JsonLocation loc = getLocation(); |
||||||
|
String suffix = getMessageSuffix(); |
||||||
|
// mild optimization, if nothing extra is needed:
|
||||||
|
if (loc != null || suffix != null) { |
||||||
|
StringBuilder sb = new StringBuilder(100); |
||||||
|
sb.append(msg); |
||||||
|
if (suffix != null) { |
||||||
|
sb.append(suffix); |
||||||
|
} |
||||||
|
if (loc != null) { |
||||||
|
sb.append('\n'); |
||||||
|
sb.append(" at "); |
||||||
|
sb.append(loc.toString()); |
||||||
|
} |
||||||
|
msg = sb.toString(); |
||||||
|
} |
||||||
|
return msg; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return getClass().getName()+": "+getMessage(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Shared base class for streaming processing contexts used during |
||||||
|
* reading and writing of Json content using Streaming API. |
||||||
|
* This context is also exposed to applications: |
||||||
|
* context object can be used by applications to get an idea of |
||||||
|
* relative position of the parser/generator within json content |
||||||
|
* being processed. This allows for some contextual processing: for |
||||||
|
* example, output within Array context can differ from that of |
||||||
|
* Object context. |
||||||
|
*/ |
||||||
|
public abstract class JsonStreamContext |
||||||
|
{ |
||||||
|
// // // Type constants used internally
|
||||||
|
|
||||||
|
protected final static int TYPE_ROOT = 0; |
||||||
|
protected final static int TYPE_ARRAY = 1; |
||||||
|
protected final static int TYPE_OBJECT = 2; |
||||||
|
|
||||||
|
protected int _type; |
||||||
|
|
||||||
|
/** |
||||||
|
* Index of the currently processed entry. Starts with -1 to signal |
||||||
|
* that no entries have been started, and gets advanced each |
||||||
|
* time a new entry is started, either by encountering an expected |
||||||
|
* separator, or with new values if no separators are expected |
||||||
|
* (the case for root context). |
||||||
|
*/ |
||||||
|
protected int _index; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected JsonStreamContext() { } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, accessors |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for finding parent context of this context; will |
||||||
|
* return null for root context. |
||||||
|
*/ |
||||||
|
public abstract JsonStreamContext getParent(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true if this context is an Array context; |
||||||
|
* that is, content is being read from or written to a Json Array. |
||||||
|
*/ |
||||||
|
public final boolean inArray() { return _type == TYPE_ARRAY; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true if this context is a Root context; |
||||||
|
* that is, content is being read from or written to without |
||||||
|
* enclosing array or object structure. |
||||||
|
*/ |
||||||
|
public final boolean inRoot() { return _type == TYPE_ROOT; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true if this context is an Object context; |
||||||
|
* that is, content is being read from or written to a Json Object. |
||||||
|
*/ |
||||||
|
public final boolean inObject() { return _type == TYPE_OBJECT; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing simple type description of current context; |
||||||
|
* either ROOT (for root-level values), OBJECT (for field names and |
||||||
|
* values of JSON Objects) or ARRAY (for values of JSON Arrays) |
||||||
|
*/ |
||||||
|
public final String getTypeDesc() { |
||||||
|
switch (_type) { |
||||||
|
case TYPE_ROOT: return "ROOT"; |
||||||
|
case TYPE_ARRAY: return "ARRAY"; |
||||||
|
case TYPE_OBJECT: return "OBJECT"; |
||||||
|
} |
||||||
|
return "?"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Number of entries that are complete and started. |
||||||
|
*/ |
||||||
|
public final int getEntryCount() |
||||||
|
{ |
||||||
|
return _index + 1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Index of the currently processed entry, if any |
||||||
|
*/ |
||||||
|
public final int getCurrentIndex() |
||||||
|
{ |
||||||
|
return (_index < 0) ? 0 : _index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing name associated with the current location. |
||||||
|
* Non-null for <code>FIELD_NAME</code> and value events that directly |
||||||
|
* follow field names; null for root level and array values. |
||||||
|
*/ |
||||||
|
public abstract String getCurrentName(); |
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Enumeration for basic token types used for returning results |
||||||
|
* of parsing JSON content. |
||||||
|
*/ |
||||||
|
public enum JsonToken |
||||||
|
{ |
||||||
|
/* Some notes on implementation: |
||||||
|
* |
||||||
|
* - Entries are to be ordered such that start/end array/object |
||||||
|
* markers come first, then field name marker (if any), and |
||||||
|
* finally scalar value tokens. This is assumed by some |
||||||
|
* typing checks. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* NOT_AVAILABLE can be returned if {@link JsonParser} |
||||||
|
* implementation can not currently return the requested |
||||||
|
* token (usually next one), or even if any will be |
||||||
|
* available, but that may be able to determine this in |
||||||
|
* future. This is the case with non-blocking parsers -- |
||||||
|
* they can not block to wait for more data to parse and |
||||||
|
* must return something. |
||||||
|
*/ |
||||||
|
NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE), |
||||||
|
|
||||||
|
/** |
||||||
|
* START_OBJECT is returned when encountering '{' |
||||||
|
* which signals starting of an Object value. |
||||||
|
*/ |
||||||
|
START_OBJECT("{", JsonTokenId.ID_START_OBJECT), |
||||||
|
|
||||||
|
/** |
||||||
|
* END_OBJECT is returned when encountering '}' |
||||||
|
* which signals ending of an Object value |
||||||
|
*/ |
||||||
|
END_OBJECT("}", JsonTokenId.ID_END_OBJECT), |
||||||
|
|
||||||
|
/** |
||||||
|
* START_ARRAY is returned when encountering '[' |
||||||
|
* which signals starting of an Array value |
||||||
|
*/ |
||||||
|
START_ARRAY("[", JsonTokenId.ID_START_ARRAY), |
||||||
|
|
||||||
|
/** |
||||||
|
* END_ARRAY is returned when encountering ']' |
||||||
|
* which signals ending of an Array value |
||||||
|
*/ |
||||||
|
END_ARRAY("]", JsonTokenId.ID_END_ARRAY), |
||||||
|
|
||||||
|
/** |
||||||
|
* FIELD_NAME is returned when a String token is encountered |
||||||
|
* as a field name (same lexical value, different function) |
||||||
|
*/ |
||||||
|
FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME), |
||||||
|
|
||||||
|
/** |
||||||
|
* Placeholder token returned when the input source has a concept |
||||||
|
* of embedded Object that are not accessible as usual structure |
||||||
|
* (of starting with {@link #START_OBJECT}, having values, ending with |
||||||
|
* {@link #END_OBJECT}), but as "raw" objects. |
||||||
|
*<p> |
||||||
|
* Note: this token is never returned by regular JSON readers, but |
||||||
|
* only by readers that expose other kinds of source (like |
||||||
|
* <code>JsonNode</code>-based JSON trees, Maps, Lists and such). |
||||||
|
*/ |
||||||
|
VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT), |
||||||
|
|
||||||
|
/** |
||||||
|
* VALUE_STRING is returned when a String token is encountered |
||||||
|
* in value context (array element, field value, or root-level |
||||||
|
* stand-alone value) |
||||||
|
*/ |
||||||
|
VALUE_STRING(null, JsonTokenId.ID_STRING), |
||||||
|
|
||||||
|
/** |
||||||
|
* VALUE_NUMBER_INT is returned when an integer numeric token is |
||||||
|
* encountered in value context: that is, a number that does |
||||||
|
* not have floating point or exponent marker in it (consists |
||||||
|
* only of an optional sign, followed by one or more digits) |
||||||
|
*/ |
||||||
|
VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT), |
||||||
|
|
||||||
|
/** |
||||||
|
* VALUE_NUMBER_INT is returned when a numeric token other |
||||||
|
* that is not an integer is encountered: that is, a number that does |
||||||
|
* have floating point or exponent marker in it, in addition |
||||||
|
* to one or more digits. |
||||||
|
*/ |
||||||
|
VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT), |
||||||
|
|
||||||
|
/** |
||||||
|
* VALUE_TRUE is returned when encountering literal "true" in |
||||||
|
* value context |
||||||
|
*/ |
||||||
|
VALUE_TRUE("true", JsonTokenId.ID_TRUE), |
||||||
|
|
||||||
|
/** |
||||||
|
* VALUE_FALSE is returned when encountering literal "false" in |
||||||
|
* value context |
||||||
|
*/ |
||||||
|
VALUE_FALSE("false", JsonTokenId.ID_FALSE), |
||||||
|
|
||||||
|
/** |
||||||
|
* VALUE_NULL is returned when encountering literal "null" in |
||||||
|
* value context |
||||||
|
*/ |
||||||
|
VALUE_NULL("null", JsonTokenId.ID_NULL), |
||||||
|
; |
||||||
|
|
||||||
|
final String _serialized; |
||||||
|
|
||||||
|
final char[] _serializedChars; |
||||||
|
|
||||||
|
final byte[] _serializedBytes; |
||||||
|
|
||||||
|
final int _id; |
||||||
|
|
||||||
|
final boolean _isStructStart, _isStructEnd; |
||||||
|
|
||||||
|
final boolean _isNumber; |
||||||
|
|
||||||
|
final boolean _isBoolean; |
||||||
|
|
||||||
|
final boolean _isScalar; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param token representation for this token, if there is a |
||||||
|
* single static representation; null otherwise |
||||||
|
*/ |
||||||
|
JsonToken(String token, int id) |
||||||
|
{ |
||||||
|
if (token == null) { |
||||||
|
_serialized = null; |
||||||
|
_serializedChars = null; |
||||||
|
_serializedBytes = null; |
||||||
|
} else { |
||||||
|
_serialized = token; |
||||||
|
_serializedChars = token.toCharArray(); |
||||||
|
// It's all in ascii, can just case...
|
||||||
|
int len = _serializedChars.length; |
||||||
|
_serializedBytes = new byte[len]; |
||||||
|
for (int i = 0; i < len; ++i) { |
||||||
|
_serializedBytes[i] = (byte) _serializedChars[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
_id = id; |
||||||
|
|
||||||
|
_isBoolean = (id == JsonTokenId.ID_FALSE || id == JsonTokenId.ID_TRUE); |
||||||
|
_isNumber = (id == JsonTokenId.ID_NUMBER_INT || id == JsonTokenId.ID_NUMBER_FLOAT); |
||||||
|
|
||||||
|
_isStructStart = (id == JsonTokenId.ID_START_OBJECT || id == JsonTokenId.ID_START_ARRAY); |
||||||
|
_isStructEnd = (id == JsonTokenId.ID_END_OBJECT || id == JsonTokenId.ID_END_ARRAY); |
||||||
|
|
||||||
|
_isScalar = !_isStructStart && !_isStructEnd |
||||||
|
&& (id != JsonTokenId.ID_FIELD_NAME) |
||||||
|
&& (id != JsonTokenId.ID_NOT_AVAILABLE); |
||||||
|
} |
||||||
|
|
||||||
|
public final int id() { return _id; } |
||||||
|
|
||||||
|
public final String asString() { return _serialized; } |
||||||
|
public final char[] asCharArray() { return _serializedChars; } |
||||||
|
public final byte[] asByteArray() { return _serializedBytes; } |
||||||
|
|
||||||
|
public final boolean isNumeric() { |
||||||
|
return _isNumber; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor that is functionally equivalent to: |
||||||
|
* <code> |
||||||
|
* this == JsonToken.START_OBJECT || this == JsonToken.START_ARRAY |
||||||
|
* </code> |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
public final boolean isStructStart() { |
||||||
|
return _isStructStart; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor that is functionally equivalent to: |
||||||
|
* <code> |
||||||
|
* this == JsonToken.END_OBJECT || this == JsonToken.END_ARRAY |
||||||
|
* </code> |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
public final boolean isStructEnd() { |
||||||
|
return _isStructEnd; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that can be used to check whether this token represents |
||||||
|
* a valid non-structured value. This means all tokens other than |
||||||
|
* Object/Array start/end markers all field names. |
||||||
|
*/ |
||||||
|
public final boolean isScalarValue() { |
||||||
|
return _isScalar; |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean isBoolean() { |
||||||
|
return _isBoolean; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface defined to contain ids accessible with {@link JsonToken#id()}. |
||||||
|
* Needed because it is impossible to define these constants in |
||||||
|
* {@link JsonToken} itself, as static constants (oddity of how Enums |
||||||
|
* are implemented by JVM). |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
public interface JsonTokenId |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#NOT_AVAILABLE}, used in |
||||||
|
* cases where a token may become available when more input |
||||||
|
* is available: this occurs in non-blocking use cases. |
||||||
|
*/ |
||||||
|
public final static int ID_NOT_AVAILABLE = -1; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent the case where no {@link JsonToken} |
||||||
|
* is available: either because {@link JsonParser} has not been |
||||||
|
* advanced to first token, or because no more tokens will be |
||||||
|
* available (end-of-input or explicit closing of parser}. |
||||||
|
*/ |
||||||
|
public final static int ID_NO_TOKEN = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#START_OBJECT} |
||||||
|
*/ |
||||||
|
public final static int ID_START_OBJECT = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#END_OBJECT} |
||||||
|
*/ |
||||||
|
public final static int ID_END_OBJECT = 2; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#START_ARRAY} |
||||||
|
*/ |
||||||
|
public final static int ID_START_ARRAY = 3; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#END_ARRAY} |
||||||
|
*/ |
||||||
|
public final static int ID_END_ARRAY = 4; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#FIELD_NAME} |
||||||
|
*/ |
||||||
|
public final static int ID_FIELD_NAME = 5; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#VALUE_STRING} |
||||||
|
*/ |
||||||
|
public final static int ID_STRING = 6; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#VALUE_NUMBER_INT} |
||||||
|
*/ |
||||||
|
public final static int ID_NUMBER_INT = 7; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#VALUE_NUMBER_FLOAT} |
||||||
|
*/ |
||||||
|
public final static int ID_NUMBER_FLOAT = 8; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#VALUE_TRUE} |
||||||
|
*/ |
||||||
|
public final static int ID_TRUE = 9; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#VALUE_FALSE} |
||||||
|
*/ |
||||||
|
public final static int ID_FALSE = 10; |
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#VALUE_NULL} |
||||||
|
*/ |
||||||
|
|
||||||
|
public final static int ID_NULL = 11; |
||||||
|
|
||||||
|
/** |
||||||
|
* Id used to represent {@link JsonToken#VALUE_EMBEDDED_OBJECT} |
||||||
|
*/ |
||||||
|
public final static int ID_EMBEDDED_OBJECT = 12; |
||||||
|
} |
@ -0,0 +1,185 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Iterator; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.type.ResolvedType; |
||||||
|
import com.fr.third.fasterxml.jackson.core.type.TypeReference; |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract class that defines the interface that {@link JsonParser} and |
||||||
|
* {@link JsonGenerator} use to serialize and deserialize regular |
||||||
|
* Java objects (POJOs aka Beans). |
||||||
|
*<p> |
||||||
|
* The standard implementation of this class is |
||||||
|
* <code>com.fr.third.fasterxml.jackson.databind.ObjectMapper</code>, |
||||||
|
* defined in the "jackson-databind". |
||||||
|
*/ |
||||||
|
public abstract class ObjectCodec |
||||||
|
extends TreeCodec // since 2.3
|
||||||
|
implements Versioned // since 2.3
|
||||||
|
{ |
||||||
|
protected ObjectCodec() { } |
||||||
|
|
||||||
|
// Since 2.3: need baseline implementation to avoid backwards compatibility
|
||||||
|
@Override |
||||||
|
public Version version() { |
||||||
|
return Version.unknownVersion(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* API for de-serialization (JSON-to-Object) |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to deserialize JSON content into a non-container |
||||||
|
* type (it can be an array type, however): typically a bean, array |
||||||
|
* or a wrapper type (like {@link java.lang.Boolean}). |
||||||
|
*<p> |
||||||
|
* Note: this method should NOT be used if the result type is a |
||||||
|
* container ({@link java.util.Collection} or {@link java.util.Map}. |
||||||
|
* The reason is that due to type erasure, key and value types |
||||||
|
* can not be introspected when using this method. |
||||||
|
*/ |
||||||
|
public abstract <T> T readValue(JsonParser jp, Class<T> valueType) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to deserialize JSON content into a Java type, reference |
||||||
|
* to which is passed as argument. Type is passed using so-called |
||||||
|
* "super type token" |
||||||
|
* and specifically needs to be used if the root type is a |
||||||
|
* parameterized (generic) container type. |
||||||
|
*/ |
||||||
|
public abstract <T> T readValue(JsonParser jp, TypeReference<?> valueTypeRef) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to deserialize JSON content into a POJO, type specified |
||||||
|
* with fully resolved type object (so it can be a generic type, |
||||||
|
* including containers like {@link java.util.Collection} and |
||||||
|
* {@link java.util.Map}). |
||||||
|
*/ |
||||||
|
public abstract <T> T readValue(JsonParser jp, ResolvedType valueType) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for reading sequence of Objects from parser stream, |
||||||
|
* all with same specified value type. |
||||||
|
*/ |
||||||
|
public abstract <T> Iterator<T> readValues(JsonParser jp, Class<T> valueType) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for reading sequence of Objects from parser stream, |
||||||
|
* all with same specified value type. |
||||||
|
*/ |
||||||
|
public abstract <T> Iterator<T> readValues(JsonParser jp, TypeReference<?> valueTypeRef) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for reading sequence of Objects from parser stream, |
||||||
|
* all with same specified value type. |
||||||
|
*/ |
||||||
|
public abstract <T> Iterator<T> readValues(JsonParser jp, ResolvedType valueType) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* API for serialization (Object-to-JSON) |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to serialize given Java Object, using generator |
||||||
|
* provided. |
||||||
|
*/ |
||||||
|
public abstract void writeValue(JsonGenerator jgen, Object value) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* TreeCodec pass-through methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to deserialize JSON content as tree expressed |
||||||
|
* using set of {@link TreeNode} instances. Returns |
||||||
|
* root of the resulting tree (where root can consist |
||||||
|
* of just a single node if the current event is a |
||||||
|
* value event, not container). |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public abstract <T extends TreeNode> T readTree(JsonParser jp) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract void writeTree(JsonGenerator jg, TreeNode tree) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for construct root level Object nodes |
||||||
|
* for Tree Model instances. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public abstract TreeNode createObjectNode(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for construct root level Array nodes |
||||||
|
* for Tree Model instances. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public abstract TreeNode createArrayNode(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for constructing a {@link JsonParser} for reading |
||||||
|
* contents of a JSON tree, as if it was external serialized |
||||||
|
* JSON content. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public abstract JsonParser treeAsTokens(TreeNode n); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Extended tree conversions beyond TreeCodec |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method for converting given JSON tree into instance of specified |
||||||
|
* value type. This is equivalent to first constructing a {@link JsonParser} to |
||||||
|
* iterate over contents of the tree, and using that parser for data binding. |
||||||
|
*/ |
||||||
|
public abstract <T> T treeToValue(TreeNode n, Class<T> valueType) |
||||||
|
throws JsonProcessingException; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Basic accessors |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @deprecated Since 2.1: Use {@link #getFactory} instead. |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public abstract JsonFactory getJsonFactory(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for finding underlying data format factory |
||||||
|
* ({@link JsonFactory}) codec will use for data binding. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public JsonFactory getFactory() { |
||||||
|
return getJsonFactory(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface for objects that implement pretty printer functionality, such |
||||||
|
* as indentation. |
||||||
|
* Pretty printers are used to add white space in output JSON content, |
||||||
|
* to make results more human readable. Usually this means things like adding |
||||||
|
* linefeeds and indentation. |
||||||
|
*<p> |
||||||
|
* Note: since Jackson 2.1, stateful implementations MUST implement |
||||||
|
* {@link com.fr.third.fasterxml.jackson.core.util.Instantiatable} interface, |
||||||
|
* to allow for constructing per-generation instances and avoid |
||||||
|
* state corruption (see [JACKSON-851] for details). |
||||||
|
* Stateless implementations need not do this; but those are less common. |
||||||
|
*/ |
||||||
|
public interface PrettyPrinter |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* First methods that act both as events, and expect |
||||||
|
/* output for correct functioning (i.e something gets |
||||||
|
/* output even when not pretty-printing) |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
// // // Root-level handling:
|
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after a root-level value has been completely |
||||||
|
* output, and before another value is to be output. |
||||||
|
*<p> |
||||||
|
* Default |
||||||
|
* handling (without pretty-printing) will output a space, to |
||||||
|
* allow values to be parsed correctly. Pretty-printer is |
||||||
|
* to output some other suitable and nice-looking separator |
||||||
|
* (tab(s), space(s), linefeed(s) or any combination thereof). |
||||||
|
*/ |
||||||
|
void writeRootValueSeparator(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
// // Object handling
|
||||||
|
|
||||||
|
/** |
||||||
|
* Method called when an Object value is to be output, before |
||||||
|
* any fields are output. |
||||||
|
*<p> |
||||||
|
* Default handling (without pretty-printing) will output |
||||||
|
* the opening curly bracket. |
||||||
|
* Pretty-printer is |
||||||
|
* to output a curly bracket as well, but can surround that |
||||||
|
* with other (white-space) decoration. |
||||||
|
*/ |
||||||
|
void writeStartObject(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after an Object value has been completely output |
||||||
|
* (minus closing curly bracket). |
||||||
|
*<p> |
||||||
|
* Default handling (without pretty-printing) will output |
||||||
|
* the closing curly bracket. |
||||||
|
* Pretty-printer is |
||||||
|
* to output a curly bracket as well, but can surround that |
||||||
|
* with other (white-space) decoration. |
||||||
|
* |
||||||
|
* @param nrOfEntries Number of direct members of the array that |
||||||
|
* have been output |
||||||
|
*/ |
||||||
|
void writeEndObject(JsonGenerator jg, int nrOfEntries) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after an object entry (field:value) has been completely |
||||||
|
* output, and before another value is to be output. |
||||||
|
*<p> |
||||||
|
* Default handling (without pretty-printing) will output a single |
||||||
|
* comma to separate the two. Pretty-printer is |
||||||
|
* to output a comma as well, but can surround that with other |
||||||
|
* (white-space) decoration. |
||||||
|
*/ |
||||||
|
void writeObjectEntrySeparator(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after an object field has been output, but |
||||||
|
* before the value is output. |
||||||
|
*<p> |
||||||
|
* Default handling (without pretty-printing) will output a single |
||||||
|
* colon to separate the two. Pretty-printer is |
||||||
|
* to output a colon as well, but can surround that with other |
||||||
|
* (white-space) decoration. |
||||||
|
*/ |
||||||
|
void writeObjectFieldValueSeparator(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
// // // Array handling
|
||||||
|
|
||||||
|
/** |
||||||
|
* Method called when an Array value is to be output, before |
||||||
|
* any member/child values are output. |
||||||
|
*<p> |
||||||
|
* Default handling (without pretty-printing) will output |
||||||
|
* the opening bracket. |
||||||
|
* Pretty-printer is |
||||||
|
* to output a bracket as well, but can surround that |
||||||
|
* with other (white-space) decoration. |
||||||
|
*/ |
||||||
|
void writeStartArray(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after an Array value has been completely output |
||||||
|
* (minus closing bracket). |
||||||
|
*<p> |
||||||
|
* Default handling (without pretty-printing) will output |
||||||
|
* the closing bracket. |
||||||
|
* Pretty-printer is |
||||||
|
* to output a bracket as well, but can surround that |
||||||
|
* with other (white-space) decoration. |
||||||
|
* |
||||||
|
* @param nrOfValues Number of direct members of the array that |
||||||
|
* have been output |
||||||
|
*/ |
||||||
|
void writeEndArray(JsonGenerator jg, int nrOfValues) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after an array value has been completely |
||||||
|
* output, and before another value is to be output. |
||||||
|
*<p> |
||||||
|
* Default handling (without pretty-printing) will output a single |
||||||
|
* comma to separate the two. Pretty-printer is |
||||||
|
* to output a comma as well, but can surround that with other |
||||||
|
* (white-space) decoration. |
||||||
|
*/ |
||||||
|
void writeArrayValueSeparator(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Then events that by default do not produce any output |
||||||
|
/* but that are often overridden to add white space |
||||||
|
/* in pretty-printing mode |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after array start marker has been output, |
||||||
|
* and right before the first value is to be output. |
||||||
|
* It is <b>not</b> called for arrays with no values. |
||||||
|
*<p> |
||||||
|
* Default handling does not output anything, but pretty-printer |
||||||
|
* is free to add any white space decoration. |
||||||
|
*/ |
||||||
|
void beforeArrayValues(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called after object start marker has been output, |
||||||
|
* and right before the field name of the first entry is |
||||||
|
* to be output. |
||||||
|
* It is <b>not</b> called for objects without entries. |
||||||
|
*<p> |
||||||
|
* Default handling does not output anything, but pretty-printer |
||||||
|
* is free to add any white space decoration. |
||||||
|
*/ |
||||||
|
void beforeObjectEntries(JsonGenerator jg) |
||||||
|
throws IOException, JsonGenerationException; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,156 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.OutputStream; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface that defines how Jackson package can interact with efficient |
||||||
|
* pre-serialized or lazily-serialized and reused String representations. |
||||||
|
* Typically implementations store possible serialized version(s) so that |
||||||
|
* serialization of String can be done more efficiently, especially when |
||||||
|
* used multiple times. |
||||||
|
*<p> |
||||||
|
* Note that "quoted" in methods means quoting of 'special' characters using |
||||||
|
* JSON backlash notation (and not use of actual double quotes). |
||||||
|
* |
||||||
|
* @see com.fr.third.fasterxml.jackson.core.io.SerializedString |
||||||
|
*/ |
||||||
|
public interface SerializableString |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Returns unquoted String that this object represents (and offers |
||||||
|
* serialized forms for) |
||||||
|
*/ |
||||||
|
String getValue(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns length of the (unquoted) String as characters. |
||||||
|
* Functionally equvalent to: |
||||||
|
*<pre> |
||||||
|
* getValue().length(); |
||||||
|
*</pre> |
||||||
|
*/ |
||||||
|
int charLength(); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Accessors for byte sequences |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns JSON quoted form of the String, as character array. |
||||||
|
* Result can be embedded as-is in textual JSON as property name or JSON String. |
||||||
|
*/ |
||||||
|
char[] asQuotedChars(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns UTF-8 encoded version of unquoted String. |
||||||
|
* Functionally equivalent to (but more efficient than): |
||||||
|
*<pre> |
||||||
|
* getValue().getBytes("UTF-8"); |
||||||
|
*</pre> |
||||||
|
*/ |
||||||
|
byte[] asUnquotedUTF8(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns UTF-8 encoded version of JSON-quoted String. |
||||||
|
* Functionally equivalent to (but more efficient than): |
||||||
|
*<pre> |
||||||
|
* new String(asQuotedChars()).getBytes("UTF-8"); |
||||||
|
*</pre> |
||||||
|
*/ |
||||||
|
byte[] asQuotedUTF8(); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Helper methods for appending byte/char sequences |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that will append quoted UTF-8 bytes of this String into given |
||||||
|
* buffer, if there is enough room; if not, returns -1. |
||||||
|
* Functionally equivalent to: |
||||||
|
*<pre> |
||||||
|
* byte[] bytes = str.asQuotedUTF8(); |
||||||
|
* System.arraycopy(bytes, 0, buffer, offset, bytes.length); |
||||||
|
* return bytes.length; |
||||||
|
*</pre> |
||||||
|
* |
||||||
|
* @return Number of bytes appended, if successful, otherwise -1 |
||||||
|
*/ |
||||||
|
int appendQuotedUTF8(byte[] buffer, int offset); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that will append quoted characters of this String into given |
||||||
|
* buffer. Functionally equivalent to: |
||||||
|
*<pre> |
||||||
|
* char[] ch = str.asQuotedChars(); |
||||||
|
* System.arraycopy(ch, 0, buffer, offset, ch.length); |
||||||
|
* return ch.length; |
||||||
|
*</pre> |
||||||
|
* |
||||||
|
* @return Number of characters appended, if successful, otherwise -1 |
||||||
|
*/ |
||||||
|
int appendQuoted(char[] buffer, int offset); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that will append unquoted ('raw') UTF-8 bytes of this String into given |
||||||
|
* buffer. Functionally equivalent to: |
||||||
|
*<pre> |
||||||
|
* byte[] bytes = str.asUnquotedUTF8(); |
||||||
|
* System.arraycopy(bytes, 0, buffer, offset, bytes.length); |
||||||
|
* return bytes.length; |
||||||
|
*</pre> |
||||||
|
* |
||||||
|
* @return Number of bytes appended, if successful, otherwise -1 |
||||||
|
*/ |
||||||
|
int appendUnquotedUTF8(byte[] buffer, int offset); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Method that will append unquoted characters of this String into given |
||||||
|
* buffer. Functionally equivalent to: |
||||||
|
*<pre> |
||||||
|
* char[] ch = str.getValue().toCharArray(); |
||||||
|
* System.arraycopy(bytes, 0, buffer, offset, ch.length); |
||||||
|
* return ch.length; |
||||||
|
*</pre> |
||||||
|
* |
||||||
|
* @return Number of characters appended, if successful, otherwise -1 |
||||||
|
*/ |
||||||
|
int appendUnquoted(char[] buffer, int offset); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Helper methods for writing out byte sequences |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Number of bytes written |
||||||
|
*/ |
||||||
|
int writeQuotedUTF8(OutputStream out) throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Number of bytes written |
||||||
|
*/ |
||||||
|
int writeUnquotedUTF8(OutputStream out) throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Number of bytes put, if successful, otherwise -1 |
||||||
|
*/ |
||||||
|
int putQuotedUTF8(ByteBuffer buffer) throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Number of bytes put, if successful, otherwise -1 |
||||||
|
*/ |
||||||
|
int putUnquotedUTF8(ByteBuffer out) throws IOException; |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface that defines objects that can read and write |
||||||
|
* {@link TreeNode} instances using Streaming API. |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
public abstract class TreeCodec |
||||||
|
{ |
||||||
|
public abstract <T extends TreeNode> T readTree(JsonParser jp) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
public abstract void writeTree(JsonGenerator jg, TreeNode tree) |
||||||
|
throws IOException, JsonProcessingException; |
||||||
|
|
||||||
|
public abstract TreeNode createArrayNode(); |
||||||
|
public abstract TreeNode createObjectNode(); |
||||||
|
|
||||||
|
public abstract JsonParser treeAsTokens(TreeNode node); |
||||||
|
} |
@ -0,0 +1,276 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
import java.util.Iterator; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker interface used to denote JSON Tree nodes, as far as |
||||||
|
* the core package knows them (which is very little): mostly |
||||||
|
* needed to allow {@link ObjectCodec} to have some level |
||||||
|
* of interoperability. |
||||||
|
* Most functionality is within <code>JsonNode</code> |
||||||
|
* base class in <code>mapper</code> package. |
||||||
|
*<p> |
||||||
|
* Note that in Jackson 1.x <code>JsonNode</code> itself |
||||||
|
* was part of core package: Jackson 2.x refactored this |
||||||
|
* since conceptually Tree Model is part of mapper package, |
||||||
|
* and so part visible to <code>core</code> package should |
||||||
|
* be minimized. |
||||||
|
*<p> |
||||||
|
* NOTE: starting with Jackson 2.2, there is more functionality |
||||||
|
* available via this class, and the intent is that this should |
||||||
|
* form actual base for multiple alternative tree representations; |
||||||
|
* for example, immutable trees could use different implementation |
||||||
|
* than mutable trees. It should also be possible to move actual |
||||||
|
* Tree Model implementation out of databind package eventually |
||||||
|
* (Jackson 3?). |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
public interface TreeNode |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Minimal introspection methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that can be used for efficient type detection |
||||||
|
* when using stream abstraction for traversing nodes. |
||||||
|
* Will return the first {@link JsonToken} that equivalent |
||||||
|
* stream event would produce (for most nodes there is just |
||||||
|
* one token but for structured/container types multiple) |
||||||
|
*/ |
||||||
|
JsonToken asToken(); |
||||||
|
|
||||||
|
/** |
||||||
|
* If this node is a numeric type (as per {@link JsonToken#isNumeric}), |
||||||
|
* returns native type that node uses to store the numeric value; |
||||||
|
* otherwise returns null. |
||||||
|
* |
||||||
|
* @return Type of number contained, if any; or null if node does not |
||||||
|
* contain numeric value. |
||||||
|
*/ |
||||||
|
JsonParser.NumberType numberType(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns number of child nodes this node contains: |
||||||
|
* for Array nodes, number of child elements, for Object nodes, |
||||||
|
* number of fields, and for all other nodes 0. |
||||||
|
* |
||||||
|
* @return For non-container nodes returns 0; for arrays number of |
||||||
|
* contained elements, and for objects number of fields. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
int size(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true for all value nodes: ones that |
||||||
|
* are not containers, and that do not represent "missing" nodes |
||||||
|
* in the path. Such value nodes represent String, Number, Boolean |
||||||
|
* and null values from JSON. |
||||||
|
*<p> |
||||||
|
* Note: one and only one of methods {@link #isValueNode}, |
||||||
|
* {@link #isContainerNode} and {@link #isMissingNode} ever |
||||||
|
* returns true for any given node. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
boolean isValueNode(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true for container nodes: Arrays and Objects. |
||||||
|
*<p> |
||||||
|
* Note: one and only one of methods {@link #isValueNode}, |
||||||
|
* {@link #isContainerNode} and {@link #isMissingNode} ever |
||||||
|
* returns true for any given node. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
boolean isContainerNode(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true for "virtual" nodes which represent |
||||||
|
* missing entries constructed by path accessor methods when |
||||||
|
* there is no actual node matching given criteria. |
||||||
|
*<p> |
||||||
|
* Note: one and only one of methods {@link #isValueNode}, |
||||||
|
* {@link #isContainerNode} and {@link #isMissingNode} ever |
||||||
|
* returns true for any given node. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
boolean isMissingNode(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true if this node is an Array node, false |
||||||
|
* otherwise. |
||||||
|
* Note that if true is returned, {@link #isContainerNode} |
||||||
|
* must also return true. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
boolean isArray(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that returns true if this node is an Object node, false |
||||||
|
* otherwise. |
||||||
|
* Note that if true is returned, {@link #isContainerNode} |
||||||
|
* must also return true. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
boolean isObject(); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Basic traversal through structured entries (Arrays, Objects) |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing value of the specified field of |
||||||
|
* an object node. If this node is not an object (or it |
||||||
|
* does not have a value for specified field name), or |
||||||
|
* if there is no field with such name, null is returned. |
||||||
|
*<p> |
||||||
|
* NOTE: handling of explicit null values may vary between |
||||||
|
* implementations; some trees may retain explicit nulls, others |
||||||
|
* not. |
||||||
|
* |
||||||
|
* @return Node that represent value of the specified field, |
||||||
|
* if this node is an object and has value for the specified |
||||||
|
* field. Null otherwise. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
TreeNode get(String fieldName); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing value of the specified element of |
||||||
|
* an array node. For other nodes, null is returned. |
||||||
|
*<p> |
||||||
|
* For array nodes, index specifies |
||||||
|
* exact location within array and allows for efficient iteration |
||||||
|
* over child elements (underlying storage is guaranteed to |
||||||
|
* be efficiently indexable, i.e. has random-access to elements). |
||||||
|
* If index is less than 0, or equal-or-greater than |
||||||
|
* <code>node.size()</code>, null is returned; no exception is |
||||||
|
* thrown for any index. |
||||||
|
* |
||||||
|
* @return Node that represent value of the specified element, |
||||||
|
* if this node is an array and has specified element. |
||||||
|
* Null otherwise. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
TreeNode get(int index); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing value of the specified field of |
||||||
|
* an object node. |
||||||
|
* For other nodes, a "missing node" (virtual node |
||||||
|
* for which {@link #isMissingNode} returns true) is returned. |
||||||
|
* |
||||||
|
* @return Node that represent value of the specified field, |
||||||
|
* if this node is an object and has value for the specified field; |
||||||
|
* otherwise "missing node" is returned. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
TreeNode path(String fieldName); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing value of the specified element of |
||||||
|
* an array node. |
||||||
|
* For other nodes, a "missing node" (virtual node |
||||||
|
* for which {@link #isMissingNode} returns true) is returned. |
||||||
|
*<p> |
||||||
|
* For array nodes, index specifies |
||||||
|
* exact location within array and allows for efficient iteration |
||||||
|
* over child elements (underlying storage is guaranteed to |
||||||
|
* be efficiently indexable, i.e. has random-access to elements). |
||||||
|
* If index is less than 0, or equal-or-greater than |
||||||
|
* <code>node.size()</code>, "missing node" is returned; no exception is |
||||||
|
* thrown for any index. |
||||||
|
* |
||||||
|
* @return Node that represent value of the specified element, |
||||||
|
* if this node is an array and has specified element; |
||||||
|
* otherwise "missing node" is returned. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
TreeNode path(int index); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing names of all fields for this node, iff |
||||||
|
* this node is an Object node. Number of field names accessible |
||||||
|
* will be {@link #size}. |
||||||
|
* |
||||||
|
* @since 2.2 |
||||||
|
*/ |
||||||
|
Iterator<String> fieldNames(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for locating node specified by given JSON pointer instances. |
||||||
|
* Method will never return null; if no matching node exists, |
||||||
|
* will return a node for which {@link TreeNode#isMissingNode()} returns true. |
||||||
|
* |
||||||
|
* @return Node that matches given JSON Pointer: if no match exists, |
||||||
|
* will return a node for which {@link TreeNode#isMissingNode()} returns true. |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
TreeNode at(JsonPointer ptr); |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method that is functionally equivalent to: |
||||||
|
*<pre> |
||||||
|
* return at(JsonPointer.valueOf(jsonPointerExpression)); |
||||||
|
*</pre> |
||||||
|
*<p> |
||||||
|
* Note that if the same expression is used often, it is preferable to construct |
||||||
|
* {@link JsonPointer} instance once and reuse it: this method will not perform |
||||||
|
* any caching of compiled expressions. |
||||||
|
* |
||||||
|
* @param jsonPointerExpression Expression to compile as a {@link JsonPointer} |
||||||
|
* instance |
||||||
|
* |
||||||
|
* @return Node that matches given JSON Pointer: if no match exists, |
||||||
|
* will return a node for which {@link TreeNode#isMissingNode()} returns true. |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
TreeNode at(String jsonPointerExpression) |
||||||
|
throws IllegalArgumentException; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Converting to/from Streaming API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for constructing a {@link JsonParser} instance for |
||||||
|
* iterating over contents of the tree that this node is root of. |
||||||
|
* Functionally equivalent to first serializing tree using |
||||||
|
* {@link ObjectCodec} and then re-parsing but |
||||||
|
* more efficient. |
||||||
|
*/ |
||||||
|
JsonParser traverse(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Same as {@link #traverse()}, but additionally passes {@link com.fr.third.fasterxml.jackson.core.ObjectCodec} |
||||||
|
* to use if {@link JsonParser#readValueAs(Class)} is used (otherwise caller must call |
||||||
|
* {@link JsonParser#setCodec} on response explicitly). |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
JsonParser traverse(ObjectCodec codec); |
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Object that encapsulates versioning information of a component. |
||||||
|
* Version information includes not just version number but also |
||||||
|
* optionally group and artifact ids of the component being versioned. |
||||||
|
*<p> |
||||||
|
* Note that optional group and artifact id properties are new with Jackson 2.0: |
||||||
|
* if provided, they should align with Maven artifact information. |
||||||
|
*/ |
||||||
|
public class Version |
||||||
|
implements Comparable<Version>, |
||||||
|
java.io.Serializable |
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
private final static Version UNKNOWN_VERSION = new Version(0, 0, 0, null, null, null); |
||||||
|
|
||||||
|
protected final int _majorVersion; |
||||||
|
|
||||||
|
protected final int _minorVersion; |
||||||
|
|
||||||
|
protected final int _patchLevel; |
||||||
|
|
||||||
|
protected final String _groupId; |
||||||
|
|
||||||
|
protected final String _artifactId; |
||||||
|
|
||||||
|
/** |
||||||
|
* Additional information for snapshot versions; null for non-snapshot |
||||||
|
* (release) versions. |
||||||
|
*/ |
||||||
|
protected final String _snapshotInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* @deprecated Use variant that takes group and artifact ids |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public Version(int major, int minor, int patchLevel, String snapshotInfo) |
||||||
|
{ |
||||||
|
this(major, minor, patchLevel, snapshotInfo, null, null); |
||||||
|
} |
||||||
|
|
||||||
|
public Version(int major, int minor, int patchLevel, String snapshotInfo, |
||||||
|
String groupId, String artifactId) |
||||||
|
{ |
||||||
|
_majorVersion = major; |
||||||
|
_minorVersion = minor; |
||||||
|
_patchLevel = patchLevel; |
||||||
|
_snapshotInfo = snapshotInfo; |
||||||
|
_groupId = (groupId == null) ? "" : groupId; |
||||||
|
_artifactId = (artifactId == null) ? "" : artifactId; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method returns canonical "not known" version, which is used as version |
||||||
|
* in cases where actual version information is not known (instead of null). |
||||||
|
*/ |
||||||
|
public static Version unknownVersion() { return UNKNOWN_VERSION; } |
||||||
|
|
||||||
|
public boolean isUknownVersion() { return (this == UNKNOWN_VERSION); } |
||||||
|
public boolean isSnapshot() { return (_snapshotInfo != null && _snapshotInfo.length() > 0); } |
||||||
|
|
||||||
|
public int getMajorVersion() { return _majorVersion; } |
||||||
|
public int getMinorVersion() { return _minorVersion; } |
||||||
|
public int getPatchLevel() { return _patchLevel; } |
||||||
|
|
||||||
|
public String getGroupId() { return _groupId; } |
||||||
|
public String getArtifactId() { return _artifactId; } |
||||||
|
|
||||||
|
public String toFullString() { |
||||||
|
return new StringBuilder() |
||||||
|
.append(_groupId) |
||||||
|
.append('/') |
||||||
|
.append(_artifactId) |
||||||
|
.append('/') |
||||||
|
.append(toString()) |
||||||
|
.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() |
||||||
|
{ |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
sb.append(_majorVersion).append('.'); |
||||||
|
sb.append(_minorVersion).append('.'); |
||||||
|
sb.append(_patchLevel); |
||||||
|
if (isSnapshot()) { |
||||||
|
sb.append('-').append(_snapshotInfo); |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
return _artifactId.hashCode() ^ _groupId.hashCode() + _majorVersion - _minorVersion + _patchLevel; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) |
||||||
|
{ |
||||||
|
if (o == this) return true; |
||||||
|
if (o == null) return false; |
||||||
|
if (o.getClass() != getClass()) return false; |
||||||
|
Version other = (Version) o; |
||||||
|
return (other._majorVersion == _majorVersion) |
||||||
|
&& (other._minorVersion == _minorVersion) |
||||||
|
&& (other._patchLevel == _patchLevel) |
||||||
|
&& other._artifactId.equals(_artifactId) |
||||||
|
&& other._groupId.equals(_groupId) |
||||||
|
; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int compareTo(Version other) |
||||||
|
{ |
||||||
|
if (other == this) return 0; |
||||||
|
|
||||||
|
int diff = _groupId.compareTo(other._groupId); |
||||||
|
if (diff == 0) { |
||||||
|
diff = _artifactId.compareTo(other._artifactId); |
||||||
|
if (diff == 0) { |
||||||
|
diff = _majorVersion - other._majorVersion; |
||||||
|
if (diff == 0) { |
||||||
|
diff = _minorVersion - other._minorVersion; |
||||||
|
if (diff == 0) { |
||||||
|
diff = _patchLevel - other._patchLevel; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return diff; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
/* Jackson JSON-processor. |
||||||
|
* |
||||||
|
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface that those Jackson components that are explicitly versioned will implement. |
||||||
|
* Intention is to allow both plug-in components (custom extensions) and applications and |
||||||
|
* frameworks that use Jackson to detect exact version of Jackson in use. |
||||||
|
* This may be useful for example for ensuring that proper Jackson version is deployed |
||||||
|
* (beyond mechanisms that deployment system may have), as well as for possible |
||||||
|
* workarounds. |
||||||
|
*/ |
||||||
|
public interface Versioned { |
||||||
|
/** |
||||||
|
* Method called to detect version of the component that implements this interface; |
||||||
|
* returned version should never be null, but may return specific "not available" |
||||||
|
* instance (see {@link Version} for details). |
||||||
|
*/ |
||||||
|
Version version(); |
||||||
|
} |
@ -0,0 +1,317 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.base; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
import com.fr.third.fasterxml.jackson.core.json.DupDetector; |
||||||
|
import com.fr.third.fasterxml.jackson.core.json.JsonWriteContext; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.DefaultPrettyPrinter; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.VersionUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* This base class implements part of API that a JSON generator exposes |
||||||
|
* to applications, adds shared internal methods that sub-classes |
||||||
|
* can use and adds some abstract methods sub-classes must implement. |
||||||
|
*/ |
||||||
|
public abstract class GeneratorBase |
||||||
|
extends JsonGenerator |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected ObjectCodec _objectCodec; |
||||||
|
|
||||||
|
/** |
||||||
|
* Bit flag composed of bits that indicate which |
||||||
|
* {@link com.fr.third.fasterxml.jackson.core.JsonGenerator.Feature}s |
||||||
|
* are enabled. |
||||||
|
*/ |
||||||
|
protected int _features; |
||||||
|
|
||||||
|
/** |
||||||
|
* Flag set to indicate that implicit conversion from number |
||||||
|
* to JSON String is needed (as per |
||||||
|
* {@link com.fr.third.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_NUMBERS_AS_STRINGS}). |
||||||
|
*/ |
||||||
|
protected boolean _cfgNumbersAsStrings; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* State |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Object that keeps track of the current contextual state |
||||||
|
* of the generator. |
||||||
|
*/ |
||||||
|
protected JsonWriteContext _writeContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* Flag that indicates whether generator is closed or not. Gets |
||||||
|
* set when it is closed by an explicit call |
||||||
|
* ({@link #close}). |
||||||
|
*/ |
||||||
|
protected boolean _closed; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected GeneratorBase(int features, ObjectCodec codec) |
||||||
|
{ |
||||||
|
super(); |
||||||
|
_features = features; |
||||||
|
DupDetector dups = Feature.STRICT_DUPLICATE_DETECTION.enabledIn(features) |
||||||
|
? DupDetector.rootDetector(this) : null; |
||||||
|
_writeContext = JsonWriteContext.createRootContext(dups); |
||||||
|
_objectCodec = codec; |
||||||
|
_cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(features); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implemented with detection that tries to find "VERSION.txt" in same |
||||||
|
* package as the implementation class. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Version version() { |
||||||
|
return VersionUtil.versionFor(getClass()); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator enable(Feature f) { |
||||||
|
_features |= f.getMask(); |
||||||
|
if (f == Feature.WRITE_NUMBERS_AS_STRINGS) { |
||||||
|
_cfgNumbersAsStrings = true; |
||||||
|
} else if (f == Feature.ESCAPE_NON_ASCII) { |
||||||
|
setHighestNonEscapedChar(127); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator disable(Feature f) { |
||||||
|
_features &= ~f.getMask(); |
||||||
|
if (f == Feature.WRITE_NUMBERS_AS_STRINGS) { |
||||||
|
_cfgNumbersAsStrings = false; |
||||||
|
} else if (f == Feature.ESCAPE_NON_ASCII) { |
||||||
|
setHighestNonEscapedChar(0); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
//public JsonGenerator configure(Feature f, boolean state) { }
|
||||||
|
|
||||||
|
@Override |
||||||
|
public final boolean isEnabled(Feature f) { |
||||||
|
return (_features & f.getMask()) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getFeatureMask() { |
||||||
|
return _features; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator setFeatureMask(int mask) { |
||||||
|
_features = mask; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator useDefaultPrettyPrinter() { |
||||||
|
/* 28-Sep-2012, tatu: As per [Issue#84], should not override a |
||||||
|
* pretty printer if one already assigned. |
||||||
|
*/ |
||||||
|
if (getPrettyPrinter() != null) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
return setPrettyPrinter(new DefaultPrettyPrinter()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator setCodec(ObjectCodec oc) { |
||||||
|
_objectCodec = oc; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final ObjectCodec getCodec() { return _objectCodec; } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, accessors |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Note: co-variant return type. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public final JsonWriteContext getOutputContext() { return _writeContext; } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, write methods, structural |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
//public void writeStartArray() throws IOException
|
||||||
|
//public void writeEndArray() throws IOException
|
||||||
|
//public void writeStartObject() throws IOException
|
||||||
|
//public void writeEndObject() throws IOException
|
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, write methods, textual |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeFieldName(SerializableString name) throws IOException { |
||||||
|
writeFieldName(name.getValue()); |
||||||
|
} |
||||||
|
|
||||||
|
//public abstract void writeString(String text) throws IOException;
|
||||||
|
|
||||||
|
//public abstract void writeString(char[] text, int offset, int len) throws IOException;
|
||||||
|
|
||||||
|
//public abstract void writeRaw(String text) throws IOException;
|
||||||
|
|
||||||
|
//public abstract void writeRaw(char[] text, int offset, int len) throws IOException;
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeString(SerializableString text) throws IOException { |
||||||
|
writeString(text.getValue()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeRawValue(String text) throws IOException { |
||||||
|
_verifyValueWrite("write raw value"); |
||||||
|
writeRaw(text); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeRawValue(String text, int offset, int len) throws IOException { |
||||||
|
_verifyValueWrite("write raw value"); |
||||||
|
writeRaw(text, offset, len); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeRawValue(char[] text, int offset, int len) throws IOException { |
||||||
|
_verifyValueWrite("write raw value"); |
||||||
|
writeRaw(text, offset, len); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException { |
||||||
|
// Let's implement this as "unsupported" to make it easier to add new parser impls
|
||||||
|
_reportUnsupportedOperation(); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, write methods, primitive |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
// Not implemented at this level, added as placeholders
|
||||||
|
|
||||||
|
/* |
||||||
|
public abstract void writeNumber(int i) |
||||||
|
public abstract void writeNumber(long l) |
||||||
|
public abstract void writeNumber(double d) |
||||||
|
public abstract void writeNumber(float f) |
||||||
|
public abstract void writeNumber(BigDecimal dec) |
||||||
|
public abstract void writeBoolean(boolean state) |
||||||
|
public abstract void writeNull() |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, write methods, POJOs, trees |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeObject(Object value) throws IOException |
||||||
|
{ |
||||||
|
if (value == null) { |
||||||
|
// important: call method that does check value write:
|
||||||
|
writeNull(); |
||||||
|
} else { |
||||||
|
/* 02-Mar-2009, tatu: we are NOT to call _verifyValueWrite here, |
||||||
|
* because that will be done when codec actually serializes |
||||||
|
* contained POJO. If we did call it it would advance state |
||||||
|
* causing exception later on |
||||||
|
*/ |
||||||
|
if (_objectCodec != null) { |
||||||
|
_objectCodec.writeValue(this, value); |
||||||
|
return; |
||||||
|
} |
||||||
|
_writeSimpleObject(value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void writeTree(TreeNode rootNode) throws IOException |
||||||
|
{ |
||||||
|
// As with 'writeObject()', we are not check if write would work
|
||||||
|
if (rootNode == null) { |
||||||
|
writeNull(); |
||||||
|
} else { |
||||||
|
if (_objectCodec == null) { |
||||||
|
throw new IllegalStateException("No ObjectCodec defined"); |
||||||
|
} |
||||||
|
_objectCodec.writeValue(this, rootNode); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, low-level output handling |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override public abstract void flush() throws IOException; |
||||||
|
@Override public void close() throws IOException { _closed = true; } |
||||||
|
@Override public boolean isClosed() { return _closed; } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Package methods for this, sub-classes |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called to release any buffers generator may be holding, |
||||||
|
* once generator is being closed. |
||||||
|
*/ |
||||||
|
protected abstract void _releaseBuffers(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called before trying to write a value (scalar or structured), |
||||||
|
* to verify that this is legal in current output state, as well as to |
||||||
|
* output separators if and as necessary. |
||||||
|
* |
||||||
|
* @param typeMsg Additional message used for generating exception message |
||||||
|
* if value output is NOT legal in current generator output state. |
||||||
|
*/ |
||||||
|
protected abstract void _verifyValueWrite(String typeMsg) throws IOException; |
||||||
|
|
||||||
|
// @Deprecated in 2.3 -- now defined in super-class; remove in 2.4
|
||||||
|
@Override |
||||||
|
protected void _writeSimpleObject(Object value) throws IOException { super._writeSimpleObject(value); } |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,574 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.base; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
import com.fr.third.fasterxml.jackson.core.JsonParser.Feature; |
||||||
|
import com.fr.third.fasterxml.jackson.core.io.NumberInput; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.ByteArrayBuilder; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.VersionUtil; |
||||||
|
|
||||||
|
import static com.fr.third.fasterxml.jackson.core.JsonTokenId.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Intermediate base class used by all Jackson {@link JsonParser} |
||||||
|
* implementations, but does not add any additional fields that depend |
||||||
|
* on particular method of obtaining input. |
||||||
|
*<p> |
||||||
|
* Note that 'minimal' here mostly refers to minimal number of fields |
||||||
|
* (size) and functionality that is specific to certain types |
||||||
|
* of parser implementations; but not necessarily to number of methods. |
||||||
|
*/ |
||||||
|
public abstract class ParserMinimalBase |
||||||
|
extends JsonParser |
||||||
|
{ |
||||||
|
// Control chars:
|
||||||
|
protected final static int INT_TAB = '\t'; |
||||||
|
protected final static int INT_LF = '\n'; |
||||||
|
protected final static int INT_CR = '\r'; |
||||||
|
protected final static int INT_SPACE = 0x0020; |
||||||
|
|
||||||
|
// Markup
|
||||||
|
protected final static int INT_LBRACKET = '['; |
||||||
|
protected final static int INT_RBRACKET = ']'; |
||||||
|
protected final static int INT_LCURLY = '{'; |
||||||
|
protected final static int INT_RCURLY = '}'; |
||||||
|
protected final static int INT_QUOTE = '"'; |
||||||
|
protected final static int INT_BACKSLASH = '\\'; |
||||||
|
protected final static int INT_SLASH = '/'; |
||||||
|
protected final static int INT_COLON = ':'; |
||||||
|
protected final static int INT_COMMA = ','; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Minimal generally useful state |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Last token retrieved via {@link #nextToken}, if any. |
||||||
|
* Null before the first call to <code>nextToken()</code>, |
||||||
|
* as well as if token has been explicitly cleared |
||||||
|
*/ |
||||||
|
protected JsonToken _currToken; |
||||||
|
|
||||||
|
/** |
||||||
|
* Last cleared token, if any: that is, value that was in |
||||||
|
* effect when {@link #clearCurrentToken} was called. |
||||||
|
*/ |
||||||
|
protected JsonToken _lastClearedToken; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected ParserMinimalBase() { } |
||||||
|
protected ParserMinimalBase(int features) { |
||||||
|
super(features); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Version version() { |
||||||
|
return VersionUtil.versionFor(getClass()); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration overrides if any |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
// from base class:
|
||||||
|
|
||||||
|
//public void enableFeature(Feature f)
|
||||||
|
//public void disableFeature(Feature f)
|
||||||
|
//public void setFeature(Feature f, boolean state)
|
||||||
|
//public boolean isFeatureEnabled(Feature f)
|
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* JsonParser impl |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract JsonToken nextToken() throws IOException, JsonParseException; |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonToken getCurrentToken() { |
||||||
|
return _currToken; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int getCurrentTokenId() { |
||||||
|
final JsonToken t = _currToken; |
||||||
|
return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean hasCurrentToken() { |
||||||
|
return _currToken != null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonToken nextValue() |
||||||
|
throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
/* Implementation should be as trivial as follows; only |
||||||
|
* needs to change if we are to skip other tokens (for |
||||||
|
* example, if comments were exposed as tokens) |
||||||
|
*/ |
||||||
|
JsonToken t = nextToken(); |
||||||
|
if (t == JsonToken.FIELD_NAME) { |
||||||
|
t = nextToken(); |
||||||
|
} |
||||||
|
return t; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonParser skipChildren() throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
if (_currToken != JsonToken.START_OBJECT |
||||||
|
&& _currToken != JsonToken.START_ARRAY) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
int open = 1; |
||||||
|
|
||||||
|
/* Since proper matching of start/end markers is handled |
||||||
|
* by nextToken(), we'll just count nesting levels here |
||||||
|
*/ |
||||||
|
while (true) { |
||||||
|
JsonToken t = nextToken(); |
||||||
|
if (t == null) { |
||||||
|
_handleEOF(); |
||||||
|
/* given constraints, above should never return; |
||||||
|
* however, FindBugs doesn't know about it and |
||||||
|
* complains... so let's add dummy break here |
||||||
|
*/ |
||||||
|
return this; |
||||||
|
} |
||||||
|
if (t.isStructStart()) { |
||||||
|
++open; |
||||||
|
} else if (t.isStructEnd()) { |
||||||
|
if (--open == 0) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method sub-classes need to implement |
||||||
|
*/ |
||||||
|
protected abstract void _handleEOF() throws JsonParseException; |
||||||
|
|
||||||
|
//public JsonToken getCurrentToken()
|
||||||
|
|
||||||
|
//public boolean hasCurrentToken()
|
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract String getCurrentName() throws IOException, JsonParseException; |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract void close() throws IOException; |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract boolean isClosed(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract JsonStreamContext getParsingContext(); |
||||||
|
|
||||||
|
// public abstract JsonLocation getTokenLocation();
|
||||||
|
|
||||||
|
// public abstract JsonLocation getCurrentLocation();
|
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, token state overrides |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clearCurrentToken() { |
||||||
|
if (_currToken != null) { |
||||||
|
_lastClearedToken = _currToken; |
||||||
|
_currToken = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonToken getLastClearedToken() { |
||||||
|
return _lastClearedToken; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract void overrideCurrentName(String name); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, access to token information, text |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract String getText() throws IOException, JsonParseException; |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract char[] getTextCharacters() throws IOException, JsonParseException; |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract boolean hasTextCharacters(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract int getTextLength() throws IOException, JsonParseException; |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract int getTextOffset() throws IOException, JsonParseException; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, access to token information, binary |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public abstract byte[] getBinaryValue(Base64Variant b64variant) |
||||||
|
throws IOException, JsonParseException; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, access with conversion/coercion |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
JsonToken t = _currToken; |
||||||
|
if (t != null) { |
||||||
|
switch (t.id()) { |
||||||
|
case ID_STRING: |
||||||
|
String str = getText().trim(); |
||||||
|
if ("true".equals(str)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
if ("false".equals(str)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (_hasTextualNull(str)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
break; |
||||||
|
case ID_NUMBER_INT: |
||||||
|
return getIntValue() != 0; |
||||||
|
case ID_TRUE: |
||||||
|
return true; |
||||||
|
case ID_FALSE: |
||||||
|
case ID_NULL: |
||||||
|
return false; |
||||||
|
case ID_EMBEDDED_OBJECT: |
||||||
|
Object value = this.getEmbeddedObject(); |
||||||
|
if (value instanceof Boolean) { |
||||||
|
return (Boolean) value; |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getValueAsInt(int defaultValue) throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
JsonToken t = _currToken; |
||||||
|
if (t != null) { |
||||||
|
switch (t.id()) { |
||||||
|
case ID_STRING: |
||||||
|
String str = getText(); |
||||||
|
if (_hasTextualNull(str)) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return NumberInput.parseAsInt(str, defaultValue); |
||||||
|
case ID_NUMBER_INT: |
||||||
|
case ID_NUMBER_FLOAT: |
||||||
|
return getIntValue(); |
||||||
|
case ID_TRUE: |
||||||
|
return 1; |
||||||
|
case ID_FALSE: |
||||||
|
return 0; |
||||||
|
case ID_NULL: |
||||||
|
return 0; |
||||||
|
case ID_EMBEDDED_OBJECT: |
||||||
|
Object value = this.getEmbeddedObject(); |
||||||
|
if (value instanceof Number) { |
||||||
|
return ((Number) value).intValue(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getValueAsLong(long defaultValue) throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
JsonToken t = _currToken; |
||||||
|
if (t != null) { |
||||||
|
switch (t.id()) { |
||||||
|
case ID_STRING: |
||||||
|
String str = getText(); |
||||||
|
if (_hasTextualNull(str)) { |
||||||
|
return 0L; |
||||||
|
} |
||||||
|
return NumberInput.parseAsLong(str, defaultValue); |
||||||
|
case ID_NUMBER_INT: |
||||||
|
case ID_NUMBER_FLOAT: |
||||||
|
return getLongValue(); |
||||||
|
case ID_TRUE: |
||||||
|
return 1L; |
||||||
|
case ID_FALSE: |
||||||
|
case ID_NULL: |
||||||
|
return 0L; |
||||||
|
case ID_EMBEDDED_OBJECT: |
||||||
|
Object value = this.getEmbeddedObject(); |
||||||
|
if (value instanceof Number) { |
||||||
|
return ((Number) value).longValue(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
JsonToken t = _currToken; |
||||||
|
if (t != null) { |
||||||
|
switch (t.id()) { |
||||||
|
case ID_STRING: |
||||||
|
String str = getText(); |
||||||
|
if (_hasTextualNull(str)) { |
||||||
|
return 0L; |
||||||
|
} |
||||||
|
return NumberInput.parseAsDouble(str, defaultValue); |
||||||
|
case ID_NUMBER_INT: |
||||||
|
case ID_NUMBER_FLOAT: |
||||||
|
return getDoubleValue(); |
||||||
|
case ID_TRUE: |
||||||
|
return 1.0; |
||||||
|
case ID_FALSE: |
||||||
|
case ID_NULL: |
||||||
|
return 0.0; |
||||||
|
case ID_EMBEDDED_OBJECT: |
||||||
|
Object value = this.getEmbeddedObject(); |
||||||
|
if (value instanceof Number) { |
||||||
|
return ((Number) value).doubleValue(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getValueAsString(String defaultValue) throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
if (_currToken != JsonToken.VALUE_STRING) { |
||||||
|
if (_currToken == null || _currToken == JsonToken.VALUE_NULL || !_currToken.isScalarValue()) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
} |
||||||
|
return getText(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Base64 decoding |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper method that can be used for base64 decoding in cases where |
||||||
|
* encoded content has already been read as a String. |
||||||
|
*/ |
||||||
|
protected void _decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant) |
||||||
|
throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
// just call helper method introduced in 2.2.3
|
||||||
|
try { |
||||||
|
b64variant.decode(str, builder); |
||||||
|
} catch (IllegalArgumentException e) { |
||||||
|
_reportError(e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param bindex Relative index within base64 character unit; between 0 |
||||||
|
* and 3 (as unit has exactly 4 characters) |
||||||
|
* |
||||||
|
* @deprecated in 2.2.3; should migrate away |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex, String msg) |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
String base; |
||||||
|
if (ch <= INT_SPACE) { |
||||||
|
base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units"; |
||||||
|
} else if (b64variant.usesPaddingChar(ch)) { |
||||||
|
base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character"; |
||||||
|
} else if (!Character.isDefined(ch) || Character.isISOControl(ch)) { |
||||||
|
// Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
|
||||||
|
base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content"; |
||||||
|
} else { |
||||||
|
base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; |
||||||
|
} |
||||||
|
if (msg != null) { |
||||||
|
base = base + ": " + msg; |
||||||
|
} |
||||||
|
throw _constructError(base); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* @deprecated in 2.2.3; should migrate away |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
protected void _reportBase64EOF() throws JsonParseException { |
||||||
|
throw _constructError("Unexpected end-of-String in base64 content"); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Coercion helper methods (overridable) |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper method used to determine whether we are currently pointing to |
||||||
|
* a String value of "null" (NOT a null token); and, if so, that parser |
||||||
|
* is to recognize and return it similar to if it was real null token. |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
protected boolean _hasTextualNull(String value) { |
||||||
|
return "null".equals(value); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Error reporting |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected void _reportUnexpectedChar(int ch, String comment) |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
if (ch < 0) { // sanity check
|
||||||
|
_reportInvalidEOF(); |
||||||
|
} |
||||||
|
String msg = "Unexpected character ("+_getCharDesc(ch)+")"; |
||||||
|
if (comment != null) { |
||||||
|
msg += ": "+comment; |
||||||
|
} |
||||||
|
_reportError(msg); |
||||||
|
} |
||||||
|
|
||||||
|
protected void _reportInvalidEOF() |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
_reportInvalidEOF(" in "+_currToken); |
||||||
|
} |
||||||
|
|
||||||
|
protected void _reportInvalidEOF(String msg) |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
_reportError("Unexpected end-of-input"+msg); |
||||||
|
} |
||||||
|
|
||||||
|
protected void _reportInvalidEOFInValue() throws JsonParseException { |
||||||
|
_reportInvalidEOF(" in a value"); |
||||||
|
} |
||||||
|
|
||||||
|
protected void _reportMissingRootWS(int ch) throws JsonParseException { |
||||||
|
_reportUnexpectedChar(ch, "Expected space separating root-level values"); |
||||||
|
} |
||||||
|
|
||||||
|
protected void _throwInvalidSpace(int i) |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
char c = (char) i; |
||||||
|
String msg = "Illegal character ("+_getCharDesc(c)+"): only regular white space (\\r, \\n, \\t) is allowed between tokens"; |
||||||
|
_reportError(msg); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called to report a problem with unquoted control character. |
||||||
|
* Note: starting with version 1.4, it is possible to suppress |
||||||
|
* exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}. |
||||||
|
*/ |
||||||
|
protected void _throwUnquotedSpace(int i, String ctxtDesc) |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
// JACKSON-208; possible to allow unquoted control chars:
|
||||||
|
if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i >= INT_SPACE) { |
||||||
|
char c = (char) i; |
||||||
|
String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc; |
||||||
|
_reportError(msg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException |
||||||
|
{ |
||||||
|
// as per [JACKSON-300]
|
||||||
|
if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) { |
||||||
|
return ch; |
||||||
|
} |
||||||
|
// and [JACKSON-548]
|
||||||
|
if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { |
||||||
|
return ch; |
||||||
|
} |
||||||
|
_reportError("Unrecognized character escape "+_getCharDesc(ch)); |
||||||
|
return ch; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Error reporting, generic |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected final static String _getCharDesc(int ch) |
||||||
|
{ |
||||||
|
char c = (char) ch; |
||||||
|
if (Character.isISOControl(c)) { |
||||||
|
return "(CTRL-CHAR, code "+ch+")"; |
||||||
|
} |
||||||
|
if (ch > 255) { |
||||||
|
return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")"; |
||||||
|
} |
||||||
|
return "'"+c+"' (code "+ch+")"; |
||||||
|
} |
||||||
|
|
||||||
|
protected final void _reportError(String msg) |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
throw _constructError(msg); |
||||||
|
} |
||||||
|
|
||||||
|
protected final void _wrapError(String msg, Throwable t) |
||||||
|
throws JsonParseException |
||||||
|
{ |
||||||
|
throw _constructError(msg, t); |
||||||
|
} |
||||||
|
|
||||||
|
protected final void _throwInternal() { |
||||||
|
VersionUtil.throwInternal(); |
||||||
|
} |
||||||
|
|
||||||
|
protected final JsonParseException _constructError(String msg, Throwable t) |
||||||
|
{ |
||||||
|
return new JsonParseException(msg, getCurrentLocation(), t); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
/** |
||||||
|
* Base classes used by concrete Parser and Generator implementations; |
||||||
|
* contain functionality that is not specific to JSON or input |
||||||
|
* abstraction (byte vs char). |
||||||
|
* Most formats extend these types, although it is also possible to |
||||||
|
* directly extend {@link com.fr.third.fasterxml.jackson.core.JsonParser} or |
||||||
|
* {@link com.fr.third.fasterxml.jackson.core.JsonGenerator}. |
||||||
|
*/ |
||||||
|
package com.fr.third.fasterxml.jackson.core.base; |
@ -0,0 +1,211 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.format; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Simple helper class that allows data format (content type) auto-detection, |
||||||
|
* given an ordered set of {@link JsonFactory} instances to use for actual low-level |
||||||
|
* detection. |
||||||
|
*/ |
||||||
|
public class DataFormatDetector |
||||||
|
{ |
||||||
|
/** |
||||||
|
* By default we will look ahead at most 64 bytes; in most cases, |
||||||
|
* much less (4 bytes or so) is needed, but we will allow bit more |
||||||
|
* leniency to support data formats that need more complex heuristics. |
||||||
|
*/ |
||||||
|
public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64; |
||||||
|
|
||||||
|
/** |
||||||
|
* Ordered list of factories which both represent data formats to |
||||||
|
* detect (in precedence order, starting with highest) and are used |
||||||
|
* for actual detection. |
||||||
|
*/ |
||||||
|
protected final JsonFactory[] _detectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* Strength of match we consider to be good enough to be used |
||||||
|
* without checking any other formats. |
||||||
|
* Default value is {@link MatchStrength#SOLID_MATCH}, |
||||||
|
*/ |
||||||
|
protected final MatchStrength _optimalMatch; |
||||||
|
|
||||||
|
/** |
||||||
|
* Strength of minimal match we accept as the answer, unless |
||||||
|
* better matches are found. |
||||||
|
* Default value is {@link MatchStrength#WEAK_MATCH}, |
||||||
|
*/ |
||||||
|
protected final MatchStrength _minimalMatch; |
||||||
|
|
||||||
|
/** |
||||||
|
* Maximum number of leading bytes of the input that we can read |
||||||
|
* to determine data format. |
||||||
|
*<p> |
||||||
|
* Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}. |
||||||
|
*/ |
||||||
|
protected final int _maxInputLookahead; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Construction |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public DataFormatDetector(JsonFactory... detectors) { |
||||||
|
this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH, |
||||||
|
DEFAULT_MAX_INPUT_LOOKAHEAD); |
||||||
|
} |
||||||
|
|
||||||
|
public DataFormatDetector(Collection<JsonFactory> detectors) { |
||||||
|
this(detectors.toArray(new JsonFactory[detectors.size()])); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that will return a detector instance that uses given |
||||||
|
* optimal match level (match that is considered sufficient to return, without |
||||||
|
* trying to find stronger matches with other formats). |
||||||
|
*/ |
||||||
|
public DataFormatDetector withOptimalMatch(MatchStrength optMatch) { |
||||||
|
if (optMatch == _optimalMatch) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
return new DataFormatDetector(_detectors, optMatch, _minimalMatch, _maxInputLookahead); |
||||||
|
} |
||||||
|
/** |
||||||
|
* Method that will return a detector instance that uses given |
||||||
|
* minimal match level; match that may be returned unless a stronger match |
||||||
|
* is found with other format detectors. |
||||||
|
*/ |
||||||
|
public DataFormatDetector withMinimalMatch(MatchStrength minMatch) { |
||||||
|
if (minMatch == _minimalMatch) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
return new DataFormatDetector(_detectors, _optimalMatch, minMatch, _maxInputLookahead); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that will return a detector instance that allows detectors to |
||||||
|
* read up to specified number of bytes when determining format match strength. |
||||||
|
*/ |
||||||
|
public DataFormatDetector withMaxInputLookahead(int lookaheadBytes) |
||||||
|
{ |
||||||
|
if (lookaheadBytes == _maxInputLookahead) { |
||||||
|
return this; |
||||||
|
} |
||||||
|
return new DataFormatDetector(_detectors, _optimalMatch, _minimalMatch, lookaheadBytes); |
||||||
|
} |
||||||
|
|
||||||
|
private DataFormatDetector(JsonFactory[] detectors, |
||||||
|
MatchStrength optMatch, MatchStrength minMatch, |
||||||
|
int maxInputLookahead) |
||||||
|
{ |
||||||
|
_detectors = detectors; |
||||||
|
_optimalMatch = optMatch; |
||||||
|
_minimalMatch = minMatch; |
||||||
|
_maxInputLookahead = maxInputLookahead; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to call to find format that content (accessible via given |
||||||
|
* {@link InputStream}) given has, as per configuration of this detector |
||||||
|
* instance. |
||||||
|
* |
||||||
|
* @return Matcher object which contains result; never null, even in cases |
||||||
|
* where no match (with specified minimal match strength) is found. |
||||||
|
*/ |
||||||
|
public DataFormatMatcher findFormat(InputStream in) throws IOException |
||||||
|
{ |
||||||
|
return _findFormat(new InputAccessor.Std(in, new byte[_maxInputLookahead])); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to call to find format that given content (full document) |
||||||
|
* has, as per configuration of this detector instance. |
||||||
|
* |
||||||
|
* @return Matcher object which contains result; never null, even in cases |
||||||
|
* where no match (with specified minimal match strength) is found. |
||||||
|
*/ |
||||||
|
public DataFormatMatcher findFormat(byte[] fullInputData) throws IOException |
||||||
|
{ |
||||||
|
return _findFormat(new InputAccessor.Std(fullInputData)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to call to find format that given content (full document) |
||||||
|
* has, as per configuration of this detector instance. |
||||||
|
* |
||||||
|
* @return Matcher object which contains result; never null, even in cases |
||||||
|
* where no match (with specified minimal match strength) is found. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public DataFormatMatcher findFormat(byte[] fullInputData, int offset, int len) throws IOException |
||||||
|
{ |
||||||
|
return _findFormat(new InputAccessor.Std(fullInputData, offset, len)); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Overrides |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() |
||||||
|
{ |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
sb.append('['); |
||||||
|
final int len = _detectors.length; |
||||||
|
if (len > 0) { |
||||||
|
sb.append(_detectors[0].getFormatName()); |
||||||
|
for (int i = 1; i < len; ++i) { |
||||||
|
sb.append(", "); |
||||||
|
sb.append(_detectors[i].getFormatName()); |
||||||
|
} |
||||||
|
} |
||||||
|
sb.append(']'); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
private DataFormatMatcher _findFormat(InputAccessor.Std acc) throws IOException |
||||||
|
{ |
||||||
|
JsonFactory bestMatch = null; |
||||||
|
MatchStrength bestMatchStrength = null; |
||||||
|
for (JsonFactory f : _detectors) { |
||||||
|
acc.reset(); |
||||||
|
MatchStrength strength = f.hasFormat(acc); |
||||||
|
// if not better than what we have so far (including minimal level limit), skip
|
||||||
|
if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
// also, needs to better match than before
|
||||||
|
if (bestMatch != null) { |
||||||
|
if (bestMatchStrength.ordinal() >= strength.ordinal()) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
// finally: if it's good enough match, we are done
|
||||||
|
bestMatch = f; |
||||||
|
bestMatchStrength = strength; |
||||||
|
if (strength.ordinal() >= _optimalMatch.ordinal()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return acc.createMatcher(bestMatch, bestMatchStrength); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.format; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.JsonFactory; |
||||||
|
import com.fr.third.fasterxml.jackson.core.JsonParser; |
||||||
|
import com.fr.third.fasterxml.jackson.core.io.MergedStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* Result object constructed by {@link DataFormatDetector} when requested |
||||||
|
* to detect format of given input data. |
||||||
|
*/ |
||||||
|
public class DataFormatMatcher |
||||||
|
{ |
||||||
|
protected final InputStream _originalStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* Content read during format matching process |
||||||
|
*/ |
||||||
|
protected final byte[] _bufferedData; |
||||||
|
|
||||||
|
/** |
||||||
|
* Pointer to the first byte in buffer available for reading |
||||||
|
*/ |
||||||
|
protected final int _bufferedStart; |
||||||
|
|
||||||
|
/** |
||||||
|
* Number of bytes available in buffer. |
||||||
|
*/ |
||||||
|
protected final int _bufferedLength; |
||||||
|
|
||||||
|
/** |
||||||
|
* Factory that produced sufficient match (if any) |
||||||
|
*/ |
||||||
|
protected final JsonFactory _match; |
||||||
|
|
||||||
|
/** |
||||||
|
* Strength of match with {@link #_match} |
||||||
|
*/ |
||||||
|
protected final MatchStrength _matchStrength; |
||||||
|
|
||||||
|
protected DataFormatMatcher(InputStream in, byte[] buffered, |
||||||
|
int bufferedStart, int bufferedLength, |
||||||
|
JsonFactory match, MatchStrength strength) |
||||||
|
{ |
||||||
|
_originalStream = in; |
||||||
|
_bufferedData = buffered; |
||||||
|
_bufferedStart = bufferedStart; |
||||||
|
_bufferedLength = bufferedLength; |
||||||
|
_match = match; |
||||||
|
_matchStrength = strength; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, simple accessors |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor to use to see if any formats matched well enough with |
||||||
|
* the input data. |
||||||
|
*/ |
||||||
|
public boolean hasMatch() { return _match != null; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing strength of the match, if any; if no match, |
||||||
|
* will return {@link MatchStrength#INCONCLUSIVE}. |
||||||
|
*/ |
||||||
|
public MatchStrength getMatchStrength() { |
||||||
|
return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for {@link JsonFactory} that represents format that data matched. |
||||||
|
*/ |
||||||
|
public JsonFactory getMatch() { return _match; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for getting brief textual name of matched format if any (null |
||||||
|
* if none). Equivalent to: |
||||||
|
*<pre> |
||||||
|
* return hasMatch() ? getMatch().getFormatName() : null; |
||||||
|
*</pre> |
||||||
|
*/ |
||||||
|
public String getMatchedFormatName() { |
||||||
|
return _match.getFormatName(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, factory methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method for trying to construct a {@link JsonParser} for |
||||||
|
* parsing content which is assumed to be in detected data format. |
||||||
|
* If no match was found, returns null. |
||||||
|
*/ |
||||||
|
public JsonParser createParserWithMatch() throws IOException { |
||||||
|
if (_match == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
if (_originalStream == null) { |
||||||
|
return _match.createParser(_bufferedData, _bufferedStart, _bufferedLength); |
||||||
|
} |
||||||
|
return _match.createParser(getDataStream()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to use for accessing input for which format detection has been done. |
||||||
|
* This <b>must</b> be used instead of using stream passed to detector |
||||||
|
* unless given stream itself can do buffering. |
||||||
|
* Stream will return all content that was read during matching process, as well |
||||||
|
* as remaining contents of the underlying stream. |
||||||
|
*/ |
||||||
|
public InputStream getDataStream() { |
||||||
|
if (_originalStream == null) { |
||||||
|
return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength); |
||||||
|
} |
||||||
|
return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,151 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.format; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.JsonFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* Interface used to expose beginning of a data file to data format |
||||||
|
* detection code. |
||||||
|
*/ |
||||||
|
public interface InputAccessor |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Method to call to check if more input is available. |
||||||
|
* Since this may result in more content to be read (at least |
||||||
|
* one more byte), a {@link IOException} may get thrown. |
||||||
|
*/ |
||||||
|
boolean hasMoreBytes() throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns next byte available, if any; if no more bytes are |
||||||
|
* available, will throw {@link java.io.EOFException}. |
||||||
|
*/ |
||||||
|
byte nextByte() throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that can be called to reset accessor to read from beginning |
||||||
|
* of input. |
||||||
|
*/ |
||||||
|
void reset(); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Standard implementation |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Basic implementation that reads data from given |
||||||
|
* {@link InputStream} and buffers it as necessary. |
||||||
|
*/ |
||||||
|
class Std implements InputAccessor |
||||||
|
{ |
||||||
|
protected final InputStream _in; |
||||||
|
|
||||||
|
protected final byte[] _buffer; |
||||||
|
|
||||||
|
protected final int _bufferedStart; |
||||||
|
|
||||||
|
/** |
||||||
|
* End of valid bytes in the buffer (points to one past last valid) |
||||||
|
*/ |
||||||
|
protected int _bufferedEnd; |
||||||
|
|
||||||
|
/** |
||||||
|
* Pointer to next available buffered byte in {@link #_buffer}. |
||||||
|
*/ |
||||||
|
protected int _ptr; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor used when content to check is available via |
||||||
|
* input stream and must be read. |
||||||
|
*/ |
||||||
|
public Std(InputStream in, byte[] buffer) |
||||||
|
{ |
||||||
|
_in = in; |
||||||
|
_buffer = buffer; |
||||||
|
_bufferedStart = 0; |
||||||
|
_ptr = 0; |
||||||
|
_bufferedEnd = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor used when the full input (or at least enough leading bytes |
||||||
|
* of full input) is available. |
||||||
|
*/ |
||||||
|
public Std(byte[] inputDocument) |
||||||
|
{ |
||||||
|
_in = null; |
||||||
|
_buffer = inputDocument; |
||||||
|
// we have it all:
|
||||||
|
_bufferedStart = 0; |
||||||
|
_bufferedEnd = inputDocument.length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor used when the full input (or at least enough leading bytes |
||||||
|
* of full input) is available. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public Std(byte[] inputDocument, int start, int len) |
||||||
|
{ |
||||||
|
_in = null; |
||||||
|
_buffer = inputDocument; |
||||||
|
_ptr = start; |
||||||
|
_bufferedStart = start; |
||||||
|
_bufferedEnd = start+len; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean hasMoreBytes() throws IOException |
||||||
|
{ |
||||||
|
if (_ptr < _bufferedEnd) { // already got more
|
||||||
|
return true; |
||||||
|
} |
||||||
|
if (_in == null) { // nowhere to read from
|
||||||
|
return false; |
||||||
|
} |
||||||
|
int amount = _buffer.length - _ptr; |
||||||
|
if (amount < 1) { // can not load any more
|
||||||
|
return false; |
||||||
|
} |
||||||
|
int count = _in.read(_buffer, _ptr, amount); |
||||||
|
if (count <= 0) { // EOF
|
||||||
|
return false; |
||||||
|
} |
||||||
|
_bufferedEnd += count; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public byte nextByte() throws IOException |
||||||
|
{ |
||||||
|
// should we just try loading more automatically?
|
||||||
|
if (_ptr >= _bufferedEnd) { |
||||||
|
if (!hasMoreBytes()) { |
||||||
|
throw new EOFException("Failed auto-detect: could not read more than "+_ptr+" bytes (max buffer size: "+_buffer.length+")"); |
||||||
|
} |
||||||
|
} |
||||||
|
return _buffer[_ptr++]; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void reset() { |
||||||
|
_ptr = _bufferedStart; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Extended API for DataFormatDetector/Matcher |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public DataFormatMatcher createMatcher(JsonFactory match, MatchStrength matchStrength) |
||||||
|
{ |
||||||
|
return new DataFormatMatcher(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart), |
||||||
|
match, matchStrength); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.format; |
||||||
|
|
||||||
|
/** |
||||||
|
* Enumeration used to indicate strength of match between data format |
||||||
|
* and piece of data (typically beginning of a data file). |
||||||
|
* Values are in increasing match strength; and detectors should return |
||||||
|
* "strongest" value: that is, it should start with strongest match |
||||||
|
* criteria, and downgrading if criteria is not fulfilled. |
||||||
|
*/ |
||||||
|
public enum MatchStrength |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Value that indicates that given data can not be in given format. |
||||||
|
*/ |
||||||
|
NO_MATCH, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that detector can not find out whether could |
||||||
|
* be a match or not. |
||||||
|
* This can occur for example for textual data formats t |
||||||
|
* when there are so many leading spaces that detector can not |
||||||
|
* find the first data byte (because detectors typically limit lookahead |
||||||
|
* to some smallish value). |
||||||
|
*/ |
||||||
|
INCONCLUSIVE, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that given data could be of specified format (i.e. |
||||||
|
* it can not be ruled out). This can occur for example when seen data |
||||||
|
* is both not in canonical formats (for example: JSON data should be a JSON Array or Object |
||||||
|
* not a scalar value, as per JSON specification) and there are known use case |
||||||
|
* where a format detected is actually used (plain JSON Strings are actually used, even |
||||||
|
* though specification does not indicate that as valid usage: as such, seeing a leading |
||||||
|
* double-quote could indicate a JSON String, which plausibly <b>could</b> indicate |
||||||
|
* non-standard JSON usage). |
||||||
|
*/ |
||||||
|
WEAK_MATCH, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that given data conforms to (one of) canonical form(s) of |
||||||
|
* the data format. |
||||||
|
*<p> |
||||||
|
* For example, when testing for XML data format, |
||||||
|
* seeing a less-than character ("<") alone (with possible leading spaces) |
||||||
|
* would be a strong indication that data could |
||||||
|
* be in xml format (but see below for {@link #FULL_MATCH} description for more) |
||||||
|
*/ |
||||||
|
SOLID_MATCH, |
||||||
|
|
||||||
|
/** |
||||||
|
* Value that indicates that given data contains a signature that is deemed |
||||||
|
* specific enough to uniquely indicate data format used. |
||||||
|
*<p> |
||||||
|
* For example, when testing for XML data format, |
||||||
|
* seing "<xml" as the first data bytes ("XML declaration", as per XML specification) |
||||||
|
* could give full confidence that data is indeed in XML format. |
||||||
|
* Not all data formats have unique leading identifiers to allow full matches; for example, |
||||||
|
* JSON only has heuristic matches and can have at most {@link #SOLID_MATCH}) match. |
||||||
|
*/ |
||||||
|
FULL_MATCH |
||||||
|
; |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
/** |
||||||
|
* Package that contains interfaces needed for dynamic, pluggable |
||||||
|
* format (auto)detection; as well as basic utility classes for |
||||||
|
* simple format detection functionality. |
||||||
|
*/ |
||||||
|
package com.fr.third.fasterxml.jackson.core.format; |
@ -0,0 +1,116 @@ |
|||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Simple basic class for optimized readers in this package; implements |
||||||
|
* "cookie-cutter" methods that are used by all actual implementations. |
||||||
|
*/ |
||||||
|
abstract class BaseReader |
||||||
|
extends Reader |
||||||
|
{ |
||||||
|
/** |
||||||
|
* JSON actually limits available Unicode range in the high end |
||||||
|
* to the same as xml (to basically limit UTF-8 max byte sequence |
||||||
|
* length to 4) |
||||||
|
*/ |
||||||
|
final protected static int LAST_VALID_UNICODE_CHAR = 0x10FFFF; |
||||||
|
|
||||||
|
final protected static char NULL_CHAR = (char) 0; |
||||||
|
final protected static char NULL_BYTE = (byte) 0; |
||||||
|
|
||||||
|
final protected IOContext _context; |
||||||
|
|
||||||
|
protected InputStream _in; |
||||||
|
|
||||||
|
protected byte[] _buffer; |
||||||
|
|
||||||
|
protected int _ptr; |
||||||
|
protected int _length; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected BaseReader(IOContext context, |
||||||
|
InputStream in, byte[] buf, int ptr, int len) |
||||||
|
{ |
||||||
|
_context = context; |
||||||
|
_in = in; |
||||||
|
_buffer = buf; |
||||||
|
_ptr = ptr; |
||||||
|
_length = len; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Reader API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() throws IOException |
||||||
|
{ |
||||||
|
InputStream in = _in; |
||||||
|
|
||||||
|
if (in != null) { |
||||||
|
_in = null; |
||||||
|
freeBuffers(); |
||||||
|
in.close(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected char[] _tmpBuf = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Although this method is implemented by the base class, AND it should |
||||||
|
* never be called by main code, let's still implement it bit more |
||||||
|
* efficiently just in case |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public int read() throws IOException |
||||||
|
{ |
||||||
|
if (_tmpBuf == null) { |
||||||
|
_tmpBuf = new char[1]; |
||||||
|
} |
||||||
|
if (read(_tmpBuf, 0, 1) < 1) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return _tmpBuf[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal/package methods: |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* This method should be called along with (or instead of) normal |
||||||
|
* close. After calling this method, no further reads should be tried. |
||||||
|
* Method will try to recycle read buffers (if any). |
||||||
|
*/ |
||||||
|
public final void freeBuffers() |
||||||
|
{ |
||||||
|
byte[] buf = _buffer; |
||||||
|
if (buf != null) { |
||||||
|
_buffer = null; |
||||||
|
_context.releaseReadIOBuffer(buf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void reportBounds(char[] cbuf, int start, int len) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
throw new ArrayIndexOutOfBoundsException("read(buf,"+start+","+len+"), cbuf["+cbuf.length+"]"); |
||||||
|
} |
||||||
|
|
||||||
|
protected void reportStrangeStream() |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
throw new IOException("Strange I/O stream, returned 0 bytes on read"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,261 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
public final class CharTypes |
||||||
|
{ |
||||||
|
private final static char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); |
||||||
|
private final static byte[] HEX_BYTES; |
||||||
|
static { |
||||||
|
int len = HEX_CHARS.length; |
||||||
|
HEX_BYTES = new byte[len]; |
||||||
|
for (int i = 0; i < len; ++i) { |
||||||
|
HEX_BYTES[i] = (byte) HEX_CHARS[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Lookup table used for determining which input characters |
||||||
|
* need special handling when contained in text segment. |
||||||
|
*/ |
||||||
|
final static int[] sInputCodes; |
||||||
|
static { |
||||||
|
/* 96 would do for most cases (backslash is ascii 94) |
||||||
|
* but if we want to do lookups by raw bytes it's better |
||||||
|
* to have full table |
||||||
|
*/ |
||||||
|
final int[] table = new int[256]; |
||||||
|
// Control chars and non-space white space are not allowed unquoted
|
||||||
|
for (int i = 0; i < 32; ++i) { |
||||||
|
table[i] = -1; |
||||||
|
} |
||||||
|
// And then string end and quote markers are special too
|
||||||
|
table['"'] = 1; |
||||||
|
table['\\'] = 1; |
||||||
|
sInputCodes = table; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Additionally we can combine UTF-8 decoding info into similar |
||||||
|
* data table. |
||||||
|
*/ |
||||||
|
final static int[] sInputCodesUTF8; |
||||||
|
static { |
||||||
|
final int[] table = new int[sInputCodes.length]; |
||||||
|
System.arraycopy(sInputCodes, 0, table, 0, table.length); |
||||||
|
for (int c = 128; c < 256; ++c) { |
||||||
|
int code; |
||||||
|
|
||||||
|
// We'll add number of bytes needed for decoding
|
||||||
|
if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
|
||||||
|
code = 2; |
||||||
|
} else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
|
||||||
|
code = 3; |
||||||
|
} else if ((c & 0xF8) == 0xF0) { |
||||||
|
// 4 bytes; double-char with surrogates and all...
|
||||||
|
code = 4; |
||||||
|
} else { |
||||||
|
// And -1 seems like a good "universal" error marker...
|
||||||
|
code = -1; |
||||||
|
} |
||||||
|
table[c] = code; |
||||||
|
} |
||||||
|
sInputCodesUTF8 = table; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* To support non-default (and -standard) unquoted field names mode, |
||||||
|
* need to have alternate checking. |
||||||
|
* Basically this is list of 8-bit ASCII characters that are legal |
||||||
|
* as part of Javascript identifier |
||||||
|
*/ |
||||||
|
final static int[] sInputCodesJsNames; |
||||||
|
static { |
||||||
|
final int[] table = new int[256]; |
||||||
|
// Default is "not a name char", mark ones that are
|
||||||
|
Arrays.fill(table, -1); |
||||||
|
// Assume rules with JS same as Java (change if/as needed)
|
||||||
|
for (int i = 33; i < 256; ++i) { |
||||||
|
if (Character.isJavaIdentifierPart((char) i)) { |
||||||
|
table[i] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
/* As per [JACKSON-267], '@', '#' and '*' are also to be accepted as well. |
||||||
|
* And '-' (for hyphenated names); and '+' for sake of symmetricity... |
||||||
|
*/ |
||||||
|
table['@'] = 0; |
||||||
|
table['#'] = 0; |
||||||
|
table['*'] = 0; |
||||||
|
table['-'] = 0; |
||||||
|
table['+'] = 0; |
||||||
|
sInputCodesJsNames = table; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This table is similar to Latin-1, except that it marks all "high-bit" |
||||||
|
* code as ok. They will be validated at a later point, when decoding |
||||||
|
* name |
||||||
|
*/ |
||||||
|
final static int[] sInputCodesUtf8JsNames; |
||||||
|
static { |
||||||
|
final int[] table = new int[256]; |
||||||
|
// start with 8-bit JS names
|
||||||
|
System.arraycopy(sInputCodesJsNames, 0, table, 0, table.length); |
||||||
|
Arrays.fill(table, 128, 128, 0); |
||||||
|
sInputCodesUtf8JsNames = table; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decoding table used to quickly determine characters that are |
||||||
|
* relevant within comment content. |
||||||
|
*/ |
||||||
|
final static int[] sInputCodesComment; |
||||||
|
static { |
||||||
|
final int[] buf = new int[256]; |
||||||
|
// but first: let's start with UTF-8 multi-byte markers:
|
||||||
|
System.arraycopy(sInputCodesUTF8, 128, buf, 128, 128); |
||||||
|
|
||||||
|
// default (0) means "ok" (skip); -1 invalid, others marked by char itself
|
||||||
|
Arrays.fill(buf, 0, 32, -1); // invalid white space
|
||||||
|
buf['\t'] = 0; // tab is still fine
|
||||||
|
buf['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment
|
||||||
|
buf['\r'] = '\r'; |
||||||
|
buf['*'] = '*'; // end marker for c-style comments
|
||||||
|
sInputCodesComment = buf; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decoding table used for skipping white space and comments. |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
final static int[] sInputCodesWS; |
||||||
|
static { |
||||||
|
// but first: let's start with UTF-8 multi-byte markers:
|
||||||
|
final int[] buf = new int[256]; |
||||||
|
System.arraycopy(sInputCodesUTF8, 128, buf, 128, 128); |
||||||
|
|
||||||
|
// default (0) means "not whitespace" (end); 1 "whitespace", -1 invalid,
|
||||||
|
// 2-4 UTF-8 multi-bytes, others marked by char itself
|
||||||
|
//
|
||||||
|
Arrays.fill(buf, 0, 32, -1); // invalid white space
|
||||||
|
buf[' '] = 1; |
||||||
|
buf['\t'] = 1; |
||||||
|
buf['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment
|
||||||
|
buf['\r'] = '\r'; |
||||||
|
buf['/'] = '/'; // start marker for c/cpp comments
|
||||||
|
buf['#'] = '#'; // start marker for YAML comments
|
||||||
|
sInputCodesWS = buf; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Lookup table used for determining which output characters in |
||||||
|
* 7-bit ASCII range need to be quoted. |
||||||
|
*/ |
||||||
|
final static int[] sOutputEscapes128; |
||||||
|
static { |
||||||
|
int[] table = new int[128]; |
||||||
|
// Control chars need generic escape sequence
|
||||||
|
for (int i = 0; i < 32; ++i) { |
||||||
|
// 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant
|
||||||
|
table[i] = CharacterEscapes.ESCAPE_STANDARD; |
||||||
|
} |
||||||
|
/* Others (and some within that range too) have explicit shorter |
||||||
|
* sequences |
||||||
|
*/ |
||||||
|
table['"'] = '"'; |
||||||
|
table['\\'] = '\\'; |
||||||
|
// Escaping of slash is optional, so let's not add it
|
||||||
|
table[0x08] = 'b'; |
||||||
|
table[0x09] = 't'; |
||||||
|
table[0x0C] = 'f'; |
||||||
|
table[0x0A] = 'n'; |
||||||
|
table[0x0D] = 'r'; |
||||||
|
sOutputEscapes128 = table; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Lookup table for the first 128 Unicode characters (7-bit ASCII) |
||||||
|
* range. For actual hex digits, contains corresponding value; |
||||||
|
* for others -1. |
||||||
|
*/ |
||||||
|
final static int[] sHexValues = new int[128]; |
||||||
|
static { |
||||||
|
Arrays.fill(sHexValues, -1); |
||||||
|
for (int i = 0; i < 10; ++i) { |
||||||
|
sHexValues['0' + i] = i; |
||||||
|
} |
||||||
|
for (int i = 0; i < 6; ++i) { |
||||||
|
sHexValues['a' + i] = 10 + i; |
||||||
|
sHexValues['A' + i] = 10 + i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static int[] getInputCodeLatin1() { return sInputCodes; } |
||||||
|
public static int[] getInputCodeUtf8() { return sInputCodesUTF8; } |
||||||
|
|
||||||
|
public static int[] getInputCodeLatin1JsNames() { return sInputCodesJsNames; } |
||||||
|
public static int[] getInputCodeUtf8JsNames() { return sInputCodesUtf8JsNames; } |
||||||
|
|
||||||
|
public static int[] getInputCodeComment() { return sInputCodesComment; } |
||||||
|
public static int[] getInputCodeWS() { return sInputCodesWS; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for getting a read-only encoding table for first 128 Unicode |
||||||
|
* code points (single-byte UTF-8 characters). |
||||||
|
* Value of 0 means "no escaping"; other positive values that value is character |
||||||
|
* to use after backslash; and negative values that generic (backslash - u) |
||||||
|
* escaping is to be used. |
||||||
|
*/ |
||||||
|
public static int[] get7BitOutputEscapes() { return sOutputEscapes128; } |
||||||
|
|
||||||
|
public static int charToHex(int ch) |
||||||
|
{ |
||||||
|
return (ch > 127) ? -1 : sHexValues[ch]; |
||||||
|
} |
||||||
|
|
||||||
|
public static void appendQuoted(StringBuilder sb, String content) |
||||||
|
{ |
||||||
|
final int[] escCodes = sOutputEscapes128; |
||||||
|
int escLen = escCodes.length; |
||||||
|
for (int i = 0, len = content.length(); i < len; ++i) { |
||||||
|
char c = content.charAt(i); |
||||||
|
if (c >= escLen || escCodes[c] == 0) { |
||||||
|
sb.append(c); |
||||||
|
continue; |
||||||
|
} |
||||||
|
sb.append('\\'); |
||||||
|
int escCode = escCodes[c]; |
||||||
|
if (escCode < 0) { // generic quoting (hex value)
|
||||||
|
// The only negative value sOutputEscapes128 returns
|
||||||
|
// is CharacterEscapes.ESCAPE_STANDARD, which mean
|
||||||
|
// appendQuotes should encode using the Unicode encoding;
|
||||||
|
// not sure if this is the right way to encode for
|
||||||
|
// CharacterEscapes.ESCAPE_CUSTOM or other (future)
|
||||||
|
// CharacterEscapes.ESCAPE_XXX values.
|
||||||
|
|
||||||
|
// We know that it has to fit in just 2 hex chars
|
||||||
|
sb.append('u'); |
||||||
|
sb.append('0'); |
||||||
|
sb.append('0'); |
||||||
|
int value = c; // widening
|
||||||
|
sb.append(HEX_CHARS[value >> 4]); |
||||||
|
sb.append(HEX_CHARS[value & 0xF]); |
||||||
|
} else { // "named", i.e. prepend with slash
|
||||||
|
sb.append((char) escCode); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static char[] copyHexChars() |
||||||
|
{ |
||||||
|
return (char[]) HEX_CHARS.clone(); |
||||||
|
} |
||||||
|
|
||||||
|
public static byte[] copyHexBytes() |
||||||
|
{ |
||||||
|
return (byte[]) HEX_BYTES.clone(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,70 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.SerializableString; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.ArraysCompat; |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract base class that defines interface for customizing character |
||||||
|
* escaping aspects for String values, for formats that use escaping. |
||||||
|
* For JSON this applies to both property names and String values. |
||||||
|
*/ |
||||||
|
public abstract class CharacterEscapes |
||||||
|
implements java.io.Serializable // since 2.1
|
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Value used for lookup tables to indicate that matching characters |
||||||
|
* do not need to be escaped. |
||||||
|
*/ |
||||||
|
public final static int ESCAPE_NONE = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* Value used for lookup tables to indicate that matching characters |
||||||
|
* are to be escaped using standard escaping; for JSON this means |
||||||
|
* (for example) using "backslash - u" escape method. |
||||||
|
*/ |
||||||
|
public final static int ESCAPE_STANDARD = -1; |
||||||
|
|
||||||
|
/** |
||||||
|
* Value used for lookup tables to indicate that matching characters |
||||||
|
* will need custom escapes; and that another call |
||||||
|
* to {@link #getEscapeSequence} is needed to figure out exact escape |
||||||
|
* sequence to output. |
||||||
|
*/ |
||||||
|
public final static int ESCAPE_CUSTOM = -2; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method generators can call to get lookup table for determining |
||||||
|
* escape handling for first 128 characters of Unicode (ASCII |
||||||
|
* characters. Caller is not to modify contents of this array, since |
||||||
|
* this is expected to be a shared copy. |
||||||
|
* |
||||||
|
* @return Array with size of at least 128, where first 128 entries |
||||||
|
* have either one of <code>ESCAPE_xxx</code> constants, or non-zero positive |
||||||
|
* integer (meaning of which is data format specific; for JSON it means |
||||||
|
* that combination of backslash and character with that value is to be used) |
||||||
|
* to indicate that specific escape sequence is to be used. |
||||||
|
*/ |
||||||
|
public abstract int[] getEscapeCodesForAscii(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Method generators can call to get lookup table for determining |
||||||
|
* exact escape sequence to use for given character. |
||||||
|
* It can be called for any character, but typically is called for |
||||||
|
* either for ASCII characters for which custom escape |
||||||
|
* sequence is needed; or for any non-ASCII character. |
||||||
|
*/ |
||||||
|
public abstract SerializableString getEscapeSequence(int ch); |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper method that can be used to get a copy of standard JSON |
||||||
|
* escape definitions; this is useful when just wanting to slightly |
||||||
|
* customize definitions. Caller can modify this array as it sees |
||||||
|
* fit and usually returns modified instance via {@link #getEscapeCodesForAscii} |
||||||
|
*/ |
||||||
|
public static int[] standardAsciiEscapesForJSON() { |
||||||
|
int[] esc = CharTypes.get7BitOutputEscapes(); |
||||||
|
return ArraysCompat.copyOf(esc, esc.length); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,265 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.JsonEncoding; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.BufferRecycler; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.TextBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* To limit number of configuration and state objects to pass, all |
||||||
|
* contextual objects that need to be passed by the factory to |
||||||
|
* readers and writers are combined under this object. One instance |
||||||
|
* is created for each reader and writer. |
||||||
|
*<p> |
||||||
|
* NOTE: non-final since 2.4, to allow sub-classing. |
||||||
|
*/ |
||||||
|
public class IOContext |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to the source object, which can be used for displaying |
||||||
|
* location information |
||||||
|
*/ |
||||||
|
protected final Object _sourceRef; |
||||||
|
|
||||||
|
/** |
||||||
|
* Encoding used by the underlying stream, if known. |
||||||
|
*/ |
||||||
|
protected JsonEncoding _encoding; |
||||||
|
|
||||||
|
/** |
||||||
|
* Flag that indicates whether underlying input/output source/target |
||||||
|
* object is fully managed by the owner of this context (parser or |
||||||
|
* generator). If true, it is, and is to be closed by parser/generator; |
||||||
|
* if false, calling application has to do closing (unless auto-closing |
||||||
|
* feature is enabled for the parser/generator in question; in which |
||||||
|
* case it acts like the owner). |
||||||
|
*/ |
||||||
|
protected final boolean _managedResource; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Buffer handling, recycling |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Recycler used for actual allocation/deallocation/reuse |
||||||
|
*/ |
||||||
|
protected final BufferRecycler _bufferRecycler; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to the allocated I/O buffer for low-level input reading, |
||||||
|
* if any allocated. |
||||||
|
*/ |
||||||
|
protected byte[] _readIOBuffer = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to the allocated I/O buffer used for low-level |
||||||
|
* encoding-related buffering. |
||||||
|
*/ |
||||||
|
protected byte[] _writeEncodingBuffer = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to the buffer allocated for temporary use with |
||||||
|
* base64 encoding or decoding. |
||||||
|
*/ |
||||||
|
protected byte[] _base64Buffer = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to the buffer allocated for tokenization purposes, |
||||||
|
* in which character input is read, and from which it can be |
||||||
|
* further returned. |
||||||
|
*/ |
||||||
|
protected char[] _tokenCBuffer = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference to the buffer allocated for buffering it for |
||||||
|
* output, before being encoded: generally this means concatenating |
||||||
|
* output, then encoding when buffer fills up. |
||||||
|
*/ |
||||||
|
protected char[] _concatCBuffer = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference temporary buffer Parser instances need if calling |
||||||
|
* app decides it wants to access name via 'getTextCharacters' method. |
||||||
|
* Regular text buffer can not be used as it may contain textual |
||||||
|
* representation of the value token. |
||||||
|
*/ |
||||||
|
protected char[] _nameCopyBuffer = null; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public IOContext(BufferRecycler br, Object sourceRef, boolean managedResource) |
||||||
|
{ |
||||||
|
_bufferRecycler = br; |
||||||
|
_sourceRef = sourceRef; |
||||||
|
_managedResource = managedResource; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEncoding(JsonEncoding enc) { |
||||||
|
_encoding = enc; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, accessors |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public Object getSourceReference() { return _sourceRef; } |
||||||
|
public JsonEncoding getEncoding() { return _encoding; } |
||||||
|
public boolean isResourceManaged() { return _managedResource; } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, buffer management |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public TextBuffer constructTextBuffer() { |
||||||
|
return new TextBuffer(_bufferRecycler); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
*<p> |
||||||
|
* Note: the method can only be called once during its life cycle. |
||||||
|
* This is to protect against accidental sharing. |
||||||
|
*/ |
||||||
|
public byte[] allocReadIOBuffer() { |
||||||
|
_verifyAlloc(_readIOBuffer); |
||||||
|
return (_readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.READ_IO_BUFFER)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.3.2 |
||||||
|
*/ |
||||||
|
public byte[] allocReadIOBuffer(int minSize) { |
||||||
|
_verifyAlloc(_readIOBuffer); |
||||||
|
return (_readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.READ_IO_BUFFER, minSize)); |
||||||
|
} |
||||||
|
|
||||||
|
public byte[] allocWriteEncodingBuffer() { |
||||||
|
_verifyAlloc(_writeEncodingBuffer); |
||||||
|
return (_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.3.2 |
||||||
|
*/ |
||||||
|
public byte[] allocWriteEncodingBuffer(int minSize) { |
||||||
|
_verifyAlloc(_writeEncodingBuffer); |
||||||
|
return (_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER, minSize)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public byte[] allocBase64Buffer() { |
||||||
|
_verifyAlloc(_base64Buffer); |
||||||
|
return (_base64Buffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.BASE64_CODEC_BUFFER)); |
||||||
|
} |
||||||
|
|
||||||
|
public char[] allocTokenBuffer() { |
||||||
|
_verifyAlloc(_tokenCBuffer); |
||||||
|
return (_tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER)); |
||||||
|
} |
||||||
|
|
||||||
|
public char[] allocConcatBuffer() { |
||||||
|
_verifyAlloc(_concatCBuffer); |
||||||
|
return (_concatCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER)); |
||||||
|
} |
||||||
|
|
||||||
|
public char[] allocNameCopyBuffer(int minSize) { |
||||||
|
_verifyAlloc(_nameCopyBuffer); |
||||||
|
return (_nameCopyBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.NAME_COPY_BUFFER, minSize)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method to call when all the processing buffers can be safely |
||||||
|
* recycled. |
||||||
|
*/ |
||||||
|
public void releaseReadIOBuffer(byte[] buf) { |
||||||
|
if (buf != null) { |
||||||
|
/* Let's do sanity checks to ensure once-and-only-once release, |
||||||
|
* as well as avoiding trying to release buffers not owned |
||||||
|
*/ |
||||||
|
_verifyRelease(buf, _readIOBuffer); |
||||||
|
_readIOBuffer = null; |
||||||
|
_bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.READ_IO_BUFFER, buf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void releaseWriteEncodingBuffer(byte[] buf) { |
||||||
|
if (buf != null) { |
||||||
|
/* Let's do sanity checks to ensure once-and-only-once release, |
||||||
|
* as well as avoiding trying to release buffers not owned |
||||||
|
*/ |
||||||
|
_verifyRelease(buf, _writeEncodingBuffer); |
||||||
|
_writeEncodingBuffer = null; |
||||||
|
_bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER, buf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void releaseBase64Buffer(byte[] buf) { |
||||||
|
if (buf != null) { // sanity checks, release once-and-only-once, must be one owned
|
||||||
|
_verifyRelease(buf, _base64Buffer); |
||||||
|
_base64Buffer = null; |
||||||
|
_bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.BASE64_CODEC_BUFFER, buf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void releaseTokenBuffer(char[] buf) { |
||||||
|
if (buf != null) { |
||||||
|
_verifyRelease(buf, _tokenCBuffer); |
||||||
|
_tokenCBuffer = null; |
||||||
|
_bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER, buf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void releaseConcatBuffer(char[] buf) { |
||||||
|
if (buf != null) { |
||||||
|
// 14-Jan-2014, tatu: Let's actually allow upgrade of the original buffer.
|
||||||
|
_verifyRelease(buf, _concatCBuffer); |
||||||
|
_concatCBuffer = null; |
||||||
|
_bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER, buf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void releaseNameCopyBuffer(char[] buf) { |
||||||
|
if (buf != null) { |
||||||
|
// 14-Jan-2014, tatu: Let's actually allow upgrade of the original buffer.
|
||||||
|
_verifyRelease(buf, _nameCopyBuffer); |
||||||
|
_nameCopyBuffer = null; |
||||||
|
_bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.NAME_COPY_BUFFER, buf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal helpers |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected void _verifyAlloc(Object buffer) { |
||||||
|
if (buffer != null) { throw new IllegalStateException("Trying to call same allocXxx() method second time"); } |
||||||
|
} |
||||||
|
|
||||||
|
protected void _verifyRelease(byte[] toRelease, byte[] src) { |
||||||
|
if ((toRelease != src) && (toRelease.length <= src.length)) { throw wrongBuf(); } |
||||||
|
} |
||||||
|
|
||||||
|
protected void _verifyRelease(char[] toRelease, char[] src) { |
||||||
|
if ((toRelease != src) && (toRelease.length <= src.length)) { throw wrongBuf(); } |
||||||
|
} |
||||||
|
|
||||||
|
private IllegalArgumentException wrongBuf() { return new IllegalArgumentException("Trying to release buffer not owned by the context"); } |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Handler class that can be used to decorate input sources. |
||||||
|
* Typical use is to use a filter abstraction (filtered stream, |
||||||
|
* reader) around original input source, and apply additional |
||||||
|
* processing during read operations. |
||||||
|
*/ |
||||||
|
public abstract class InputDecorator |
||||||
|
implements java.io.Serializable // since 2.1
|
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called by {@link com.fr.third.fasterxml.jackson.core.JsonFactory} instance when |
||||||
|
* creating parser given an {@link InputStream}, when this decorator |
||||||
|
* has been registered. |
||||||
|
* |
||||||
|
* @param ctxt IO context in use (provides access to declared encoding). |
||||||
|
* NOTE: at this point context may not have all information initialized; |
||||||
|
* specifically auto-detected encoding is only available once parsing starts, |
||||||
|
* which may occur only after this method is called. |
||||||
|
* @param in Original input source |
||||||
|
* |
||||||
|
* @return InputStream to use; either passed in argument, or something that |
||||||
|
* calls it |
||||||
|
*/ |
||||||
|
public abstract InputStream decorate(IOContext ctxt, InputStream in) |
||||||
|
throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called by {@link com.fr.third.fasterxml.jackson.core.JsonFactory} instance when |
||||||
|
* creating parser on given "raw" byte source. |
||||||
|
* Method can either construct a {@link InputStream} for reading; or return |
||||||
|
* null to indicate that no wrapping should occur. |
||||||
|
* |
||||||
|
* @param ctxt IO context in use (provides access to declared encoding) |
||||||
|
* NOTE: at this point context may not have all information initialized; |
||||||
|
* specifically auto-detected encoding is only available once parsing starts, |
||||||
|
* which may occur only after this method is called. |
||||||
|
* @param src Input buffer that contains contents to parse |
||||||
|
* @param offset Offset of the first available byte in the input buffer |
||||||
|
* @param length Number of bytes available in the input buffer |
||||||
|
* |
||||||
|
* @return Either {@link InputStream} to use as input source; or null to indicate |
||||||
|
* that contents are to be processed as-is by caller |
||||||
|
*/ |
||||||
|
public abstract InputStream decorate(IOContext ctxt, byte[] src, int offset, int length) |
||||||
|
throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called by {@link com.fr.third.fasterxml.jackson.core.JsonFactory} instance when |
||||||
|
* creating parser given an {@link Reader}, when this decorator |
||||||
|
* has been registered. |
||||||
|
* |
||||||
|
* @param ctxt IO context in use (provides access to declared encoding) |
||||||
|
* NOTE: at this point context may not have all information initialized; |
||||||
|
* specifically auto-detected encoding is only available once parsing starts, |
||||||
|
* which may occur only after this method is called. |
||||||
|
* @param src Original input source |
||||||
|
* |
||||||
|
* @return Reader to use; either passed in argument, or something that |
||||||
|
* calls it (for example, a {@link FilterReader}) |
||||||
|
*/ |
||||||
|
public abstract Reader decorate(IOContext ctxt, Reader src) throws IOException; |
||||||
|
} |
@ -0,0 +1,396 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.lang.ref.SoftReference; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.util.BufferRecycler; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.ByteArrayBuilder; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.TextBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper class used for efficient encoding of JSON String values (including |
||||||
|
* JSON field names) into Strings or UTF-8 byte arrays. |
||||||
|
*<p> |
||||||
|
* Note that methods in here are somewhat optimized, but not ridiculously so. |
||||||
|
* Reason is that conversion method results are expected to be cached so that |
||||||
|
* these methods will not be hot spots during normal operation. |
||||||
|
*/ |
||||||
|
public final class JsonStringEncoder |
||||||
|
{ |
||||||
|
private final static char[] HEX_CHARS = CharTypes.copyHexChars(); |
||||||
|
|
||||||
|
private final static byte[] HEX_BYTES = CharTypes.copyHexBytes(); |
||||||
|
|
||||||
|
private final static int SURR1_FIRST = 0xD800; |
||||||
|
private final static int SURR1_LAST = 0xDBFF; |
||||||
|
private final static int SURR2_FIRST = 0xDC00; |
||||||
|
private final static int SURR2_LAST = 0xDFFF; |
||||||
|
|
||||||
|
private final static int INT_BACKSLASH = '\\'; |
||||||
|
private final static int INT_U = 'u'; |
||||||
|
private final static int INT_0 = '0'; |
||||||
|
|
||||||
|
/** |
||||||
|
* This <code>ThreadLocal</code> contains a {@link java.lang.ref.SoftReference} |
||||||
|
* to a {@link BufferRecycler} used to provide a low-cost |
||||||
|
* buffer recycling between reader and writer instances. |
||||||
|
*/ |
||||||
|
final protected static ThreadLocal<SoftReference<JsonStringEncoder>> _threadEncoder |
||||||
|
= new ThreadLocal<SoftReference<JsonStringEncoder>>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Lazily constructed text buffer used to produce JSON encoded Strings |
||||||
|
* as characters (without UTF-8 encoding) |
||||||
|
*/ |
||||||
|
protected TextBuffer _textBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Lazily-constructed builder used for UTF-8 encoding of text values |
||||||
|
* (quoted and unquoted) |
||||||
|
*/ |
||||||
|
protected ByteArrayBuilder _byteBuilder; |
||||||
|
|
||||||
|
/** |
||||||
|
* Temporary buffer used for composing quote/escape sequences |
||||||
|
*/ |
||||||
|
protected final char[] _quoteBuffer; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Construction, instance access |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public JsonStringEncoder() |
||||||
|
{ |
||||||
|
_quoteBuffer = new char[6]; |
||||||
|
_quoteBuffer[0] = '\\'; |
||||||
|
_quoteBuffer[2] = '0'; |
||||||
|
_quoteBuffer[3] = '0'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Factory method for getting an instance; this is either recycled per-thread instance, |
||||||
|
* or a newly constructed one. |
||||||
|
*/ |
||||||
|
public static JsonStringEncoder getInstance() |
||||||
|
{ |
||||||
|
SoftReference<JsonStringEncoder> ref = _threadEncoder.get(); |
||||||
|
JsonStringEncoder enc = (ref == null) ? null : ref.get(); |
||||||
|
|
||||||
|
if (enc == null) { |
||||||
|
enc = new JsonStringEncoder(); |
||||||
|
_threadEncoder.set(new SoftReference<JsonStringEncoder>(enc)); |
||||||
|
} |
||||||
|
return enc; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that will quote text contents using JSON standard quoting, |
||||||
|
* and return results as a character array |
||||||
|
*/ |
||||||
|
public char[] quoteAsString(String input) |
||||||
|
{ |
||||||
|
TextBuffer textBuffer = _textBuffer; |
||||||
|
if (textBuffer == null) { |
||||||
|
// no allocator; can add if we must, shouldn't need to
|
||||||
|
_textBuffer = textBuffer = new TextBuffer(null); |
||||||
|
} |
||||||
|
char[] outputBuffer = textBuffer.emptyAndGetCurrentSegment(); |
||||||
|
final int[] escCodes = CharTypes.get7BitOutputEscapes(); |
||||||
|
final int escCodeCount = escCodes.length; |
||||||
|
int inPtr = 0; |
||||||
|
final int inputLen = input.length(); |
||||||
|
int outPtr = 0; |
||||||
|
|
||||||
|
outer_loop: |
||||||
|
while (inPtr < inputLen) { |
||||||
|
tight_loop: |
||||||
|
while (true) { |
||||||
|
char c = input.charAt(inPtr); |
||||||
|
if (c < escCodeCount && escCodes[c] != 0) { |
||||||
|
break tight_loop; |
||||||
|
} |
||||||
|
if (outPtr >= outputBuffer.length) { |
||||||
|
outputBuffer = textBuffer.finishCurrentSegment(); |
||||||
|
outPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outPtr++] = c; |
||||||
|
if (++inPtr >= inputLen) { |
||||||
|
break outer_loop; |
||||||
|
} |
||||||
|
} |
||||||
|
// something to escape; 2 or 6-char variant?
|
||||||
|
char d = input.charAt(inPtr++); |
||||||
|
int escCode = escCodes[d]; |
||||||
|
int length = (escCode < 0) |
||||||
|
? _appendNumericEscape(d, _quoteBuffer) |
||||||
|
: _appendNamedEscape(escCode, _quoteBuffer); |
||||||
|
; |
||||||
|
if ((outPtr + length) > outputBuffer.length) { |
||||||
|
int first = outputBuffer.length - outPtr; |
||||||
|
if (first > 0) { |
||||||
|
System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, first); |
||||||
|
} |
||||||
|
outputBuffer = textBuffer.finishCurrentSegment(); |
||||||
|
int second = length - first; |
||||||
|
System.arraycopy(_quoteBuffer, first, outputBuffer, 0, second); |
||||||
|
outPtr = second; |
||||||
|
} else { |
||||||
|
System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, length); |
||||||
|
outPtr += length; |
||||||
|
} |
||||||
|
} |
||||||
|
textBuffer.setCurrentLength(outPtr); |
||||||
|
return textBuffer.contentsAsArray(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will quote given JSON String value using standard quoting, encode |
||||||
|
* results as UTF-8, and return result as a byte array. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("resource") |
||||||
|
public byte[] quoteAsUTF8(String text) |
||||||
|
{ |
||||||
|
ByteArrayBuilder byteBuilder = _byteBuilder; |
||||||
|
if (byteBuilder == null) { |
||||||
|
// no allocator; can add if we must, shouldn't need to
|
||||||
|
_byteBuilder = byteBuilder = new ByteArrayBuilder(null); |
||||||
|
} |
||||||
|
int inputPtr = 0; |
||||||
|
int inputEnd = text.length(); |
||||||
|
int outputPtr = 0; |
||||||
|
byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment(); |
||||||
|
|
||||||
|
main_loop: |
||||||
|
while (inputPtr < inputEnd) { |
||||||
|
final int[] escCodes = CharTypes.get7BitOutputEscapes(); |
||||||
|
|
||||||
|
inner_loop: // ASCII and escapes
|
||||||
|
while (true) { |
||||||
|
int ch = text.charAt(inputPtr); |
||||||
|
if (ch > 0x7F || escCodes[ch] != 0) { |
||||||
|
break inner_loop; |
||||||
|
} |
||||||
|
if (outputPtr >= outputBuffer.length) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) ch; |
||||||
|
if (++inputPtr >= inputEnd) { |
||||||
|
break main_loop; |
||||||
|
} |
||||||
|
} |
||||||
|
if (outputPtr >= outputBuffer.length) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
// Ok, so what did we hit?
|
||||||
|
int ch = (int) text.charAt(inputPtr++); |
||||||
|
if (ch <= 0x7F) { // needs quoting
|
||||||
|
int escape = escCodes[ch]; |
||||||
|
// ctrl-char, 6-byte escape...
|
||||||
|
outputPtr = _appendByteEscape(ch, escape, byteBuilder, outputPtr); |
||||||
|
outputBuffer = byteBuilder.getCurrentSegment(); |
||||||
|
continue main_loop; |
||||||
|
} else if (ch <= 0x7FF) { // fine, just needs 2 byte output
|
||||||
|
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); |
||||||
|
ch = (0x80 | (ch & 0x3f)); |
||||||
|
} else { // 3 or 4 bytes
|
||||||
|
// Surrogates?
|
||||||
|
if (ch < SURR1_FIRST || ch > SURR2_LAST) { // nope
|
||||||
|
outputBuffer[outputPtr++] = (byte) (0xe0 | (ch >> 12)); |
||||||
|
if (outputPtr >= outputBuffer.length) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); |
||||||
|
ch = (0x80 | (ch & 0x3f)); |
||||||
|
} else { // yes, surrogate pair
|
||||||
|
if (ch > SURR1_LAST) { // must be from first range
|
||||||
|
_illegalSurrogate(ch); |
||||||
|
} |
||||||
|
// and if so, followed by another from next range
|
||||||
|
if (inputPtr >= inputEnd) { |
||||||
|
_illegalSurrogate(ch); |
||||||
|
} |
||||||
|
ch = _convertSurrogate(ch, text.charAt(inputPtr++)); |
||||||
|
if (ch > 0x10FFFF) { // illegal, as per RFC 4627
|
||||||
|
_illegalSurrogate(ch); |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0xf0 | (ch >> 18)); |
||||||
|
if (outputPtr >= outputBuffer.length) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 12) & 0x3f)); |
||||||
|
if (outputPtr >= outputBuffer.length) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); |
||||||
|
ch = (0x80 | (ch & 0x3f)); |
||||||
|
} |
||||||
|
} |
||||||
|
if (outputPtr >= outputBuffer.length) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) ch; |
||||||
|
} |
||||||
|
return _byteBuilder.completeAndCoalesce(outputPtr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will encode given String as UTF-8 (without any quoting), return |
||||||
|
* resulting byte array. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("resource") |
||||||
|
public byte[] encodeAsUTF8(String text) |
||||||
|
{ |
||||||
|
ByteArrayBuilder byteBuilder = _byteBuilder; |
||||||
|
if (byteBuilder == null) { |
||||||
|
// no allocator; can add if we must, shouldn't need to
|
||||||
|
_byteBuilder = byteBuilder = new ByteArrayBuilder(null); |
||||||
|
} |
||||||
|
int inputPtr = 0; |
||||||
|
int inputEnd = text.length(); |
||||||
|
int outputPtr = 0; |
||||||
|
byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment(); |
||||||
|
int outputEnd = outputBuffer.length; |
||||||
|
|
||||||
|
main_loop: |
||||||
|
while (inputPtr < inputEnd) { |
||||||
|
int c = text.charAt(inputPtr++); |
||||||
|
|
||||||
|
// first tight loop for ascii
|
||||||
|
while (c <= 0x7F) { |
||||||
|
if (outputPtr >= outputEnd) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputEnd = outputBuffer.length; |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) c; |
||||||
|
if (inputPtr >= inputEnd) { |
||||||
|
break main_loop; |
||||||
|
} |
||||||
|
c = text.charAt(inputPtr++); |
||||||
|
} |
||||||
|
|
||||||
|
// then multi-byte...
|
||||||
|
if (outputPtr >= outputEnd) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputEnd = outputBuffer.length; |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
if (c < 0x800) { // 2-byte
|
||||||
|
outputBuffer[outputPtr++] = (byte) (0xc0 | (c >> 6)); |
||||||
|
} else { // 3 or 4 bytes
|
||||||
|
// Surrogates?
|
||||||
|
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope
|
||||||
|
outputBuffer[outputPtr++] = (byte) (0xe0 | (c >> 12)); |
||||||
|
if (outputPtr >= outputEnd) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputEnd = outputBuffer.length; |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
} else { // yes, surrogate pair
|
||||||
|
if (c > SURR1_LAST) { // must be from first range
|
||||||
|
_illegalSurrogate(c); |
||||||
|
} |
||||||
|
// and if so, followed by another from next range
|
||||||
|
if (inputPtr >= inputEnd) { |
||||||
|
_illegalSurrogate(c); |
||||||
|
} |
||||||
|
c = _convertSurrogate(c, text.charAt(inputPtr++)); |
||||||
|
if (c > 0x10FFFF) { // illegal, as per RFC 4627
|
||||||
|
_illegalSurrogate(c); |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0xf0 | (c >> 18)); |
||||||
|
if (outputPtr >= outputEnd) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputEnd = outputBuffer.length; |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); |
||||||
|
if (outputPtr >= outputEnd) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputEnd = outputBuffer.length; |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
} |
||||||
|
} |
||||||
|
if (outputPtr >= outputEnd) { |
||||||
|
outputBuffer = byteBuilder.finishCurrentSegment(); |
||||||
|
outputEnd = outputBuffer.length; |
||||||
|
outputPtr = 0; |
||||||
|
} |
||||||
|
outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} |
||||||
|
return _byteBuilder.completeAndCoalesce(outputPtr); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
private int _appendNumericEscape(int value, char[] quoteBuffer) |
||||||
|
{ |
||||||
|
quoteBuffer[1] = 'u'; |
||||||
|
// We know it's a control char, so only the last 2 chars are non-0
|
||||||
|
quoteBuffer[4] = HEX_CHARS[value >> 4]; |
||||||
|
quoteBuffer[5] = HEX_CHARS[value & 0xF]; |
||||||
|
return 6; |
||||||
|
} |
||||||
|
|
||||||
|
private int _appendNamedEscape(int escCode, char[] quoteBuffer) |
||||||
|
{ |
||||||
|
quoteBuffer[1] = (char) escCode; |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
private int _appendByteEscape(int ch, int escCode, ByteArrayBuilder byteBuilder, int ptr) |
||||||
|
{ |
||||||
|
byteBuilder.setCurrentSegmentLength(ptr); |
||||||
|
byteBuilder.append(INT_BACKSLASH); |
||||||
|
if (escCode < 0) { // standard escape
|
||||||
|
byteBuilder.append(INT_U); |
||||||
|
if (ch > 0xFF) { |
||||||
|
int hi = (ch >> 8); |
||||||
|
byteBuilder.append(HEX_BYTES[hi >> 4]); |
||||||
|
byteBuilder.append(HEX_BYTES[hi & 0xF]); |
||||||
|
ch &= 0xFF; |
||||||
|
} else { |
||||||
|
byteBuilder.append(INT_0); |
||||||
|
byteBuilder.append(INT_0); |
||||||
|
} |
||||||
|
byteBuilder.append(HEX_BYTES[ch >> 4]); |
||||||
|
byteBuilder.append(HEX_BYTES[ch & 0xF]); |
||||||
|
} else { // 2-char simple escape
|
||||||
|
byteBuilder.append((byte) escCode); |
||||||
|
} |
||||||
|
return byteBuilder.getCurrentSegmentLength(); |
||||||
|
} |
||||||
|
|
||||||
|
protected static int _convertSurrogate(int firstPart, int secondPart) |
||||||
|
{ |
||||||
|
// Ok, then, is the second part valid?
|
||||||
|
if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) { |
||||||
|
throw new IllegalArgumentException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination"); |
||||||
|
} |
||||||
|
return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST); |
||||||
|
} |
||||||
|
|
||||||
|
protected static void _illegalSurrogate(int code) { |
||||||
|
throw new IllegalArgumentException(UTF8Writer.illegalSurrogateDesc(code)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,145 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Simple {@link InputStream} implementation that is used to "unwind" some |
||||||
|
* data previously read from an input stream; so that as long as some of |
||||||
|
* that data remains, it's returned; but as long as it's read, we'll |
||||||
|
* just use data from the underlying original stream. |
||||||
|
* This is similar to {@link java.io.PushbackInputStream}, but here there's |
||||||
|
* only one implicit pushback, when instance is constructed. |
||||||
|
*/ |
||||||
|
public final class MergedStream |
||||||
|
extends InputStream |
||||||
|
{ |
||||||
|
final protected IOContext _context; |
||||||
|
|
||||||
|
final InputStream _in; |
||||||
|
|
||||||
|
byte[] _buffer; |
||||||
|
|
||||||
|
int _ptr; |
||||||
|
|
||||||
|
final int _end; |
||||||
|
|
||||||
|
public MergedStream(IOContext context, |
||||||
|
InputStream in, byte[] buf, int start, int end) |
||||||
|
{ |
||||||
|
_context = context; |
||||||
|
_in = in; |
||||||
|
_buffer = buf; |
||||||
|
_ptr = start; |
||||||
|
_end = end; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int available() throws IOException |
||||||
|
{ |
||||||
|
if (_buffer != null) { |
||||||
|
return _end - _ptr; |
||||||
|
} |
||||||
|
return _in.available(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() throws IOException |
||||||
|
{ |
||||||
|
freeMergedBuffer(); |
||||||
|
_in.close(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void mark(int readlimit) |
||||||
|
{ |
||||||
|
if (_buffer == null) { |
||||||
|
_in.mark(readlimit); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean markSupported() |
||||||
|
{ |
||||||
|
// Only supports marks past the initial rewindable section...
|
||||||
|
return (_buffer == null) && _in.markSupported(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int read() throws IOException |
||||||
|
{ |
||||||
|
if (_buffer != null) { |
||||||
|
int c = _buffer[_ptr++] & 0xFF; |
||||||
|
if (_ptr >= _end) { |
||||||
|
freeMergedBuffer(); |
||||||
|
} |
||||||
|
return c; |
||||||
|
} |
||||||
|
return _in.read(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int read(byte[] b) throws IOException |
||||||
|
{ |
||||||
|
return read(b, 0, b.length); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int read(byte[] b, int off, int len) throws IOException |
||||||
|
{ |
||||||
|
if (_buffer != null) { |
||||||
|
int avail = _end - _ptr; |
||||||
|
if (len > avail) { |
||||||
|
len = avail; |
||||||
|
} |
||||||
|
System.arraycopy(_buffer, _ptr, b, off, len); |
||||||
|
_ptr += len; |
||||||
|
if (_ptr >= _end) { |
||||||
|
freeMergedBuffer(); |
||||||
|
} |
||||||
|
return len; |
||||||
|
} |
||||||
|
return _in.read(b, off, len); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void reset() throws IOException |
||||||
|
{ |
||||||
|
if (_buffer == null) { |
||||||
|
_in.reset(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long skip(long n) throws IOException |
||||||
|
{ |
||||||
|
long count = 0L; |
||||||
|
|
||||||
|
if (_buffer != null) { |
||||||
|
int amount = _end - _ptr; |
||||||
|
|
||||||
|
if (amount > n) { // all in pushed back segment?
|
||||||
|
_ptr += (int) n; |
||||||
|
return n; |
||||||
|
} |
||||||
|
freeMergedBuffer(); |
||||||
|
count += amount; |
||||||
|
n -= amount; |
||||||
|
} |
||||||
|
|
||||||
|
if (n > 0) { |
||||||
|
count += _in.skip(n); |
||||||
|
} |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
private void freeMergedBuffer() |
||||||
|
{ |
||||||
|
byte[] buf = _buffer; |
||||||
|
if (buf != null) { |
||||||
|
_buffer = null; |
||||||
|
if (_context != null) { |
||||||
|
_context.releaseReadIOBuffer(buf); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,319 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.math.BigDecimal; |
||||||
|
|
||||||
|
public final class NumberInput |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Textual representation of a double constant that can cause nasty problems |
||||||
|
* with JDK (see http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308).
|
||||||
|
*/ |
||||||
|
public final static String NASTY_SMALL_DOUBLE = "2.2250738585072012e-308"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constants needed for parsing longs from basic int parsing methods |
||||||
|
*/ |
||||||
|
final static long L_BILLION = 1000000000; |
||||||
|
|
||||||
|
final static String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE).substring(1); |
||||||
|
final static String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE); |
||||||
|
|
||||||
|
/** |
||||||
|
* Fast method for parsing integers that are known to fit into |
||||||
|
* regular 32-bit signed int type. This means that length is |
||||||
|
* between 1 and 9 digits (inclusive) |
||||||
|
*<p> |
||||||
|
* Note: public to let unit tests call it |
||||||
|
*/ |
||||||
|
public static int parseInt(char[] digitChars, int offset, int len) |
||||||
|
{ |
||||||
|
int num = digitChars[offset] - '0'; |
||||||
|
len += offset; |
||||||
|
// This looks ugly, but appears the fastest way (as per measurements)
|
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
if (++offset < len) { |
||||||
|
num = (num * 10) + (digitChars[offset] - '0'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return num; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper method to (more) efficiently parse integer numbers from |
||||||
|
* String values. |
||||||
|
*/ |
||||||
|
public static int parseInt(String str) |
||||||
|
{ |
||||||
|
/* Ok: let's keep strategy simple: ignoring optional minus sign, |
||||||
|
* we'll accept 1 - 9 digits and parse things efficiently; |
||||||
|
* otherwise just defer to JDK parse functionality. |
||||||
|
*/ |
||||||
|
char c = str.charAt(0); |
||||||
|
int length = str.length(); |
||||||
|
boolean negative = (c == '-'); |
||||||
|
int offset = 1; |
||||||
|
// must have 1 - 9 digits after optional sign:
|
||||||
|
// negative?
|
||||||
|
if (negative) { |
||||||
|
if (length == 1 || length > 10) { |
||||||
|
return Integer.parseInt(str); |
||||||
|
} |
||||||
|
c = str.charAt(offset++); |
||||||
|
} else { |
||||||
|
if (length > 9) { |
||||||
|
return Integer.parseInt(str); |
||||||
|
} |
||||||
|
} |
||||||
|
if (c > '9' || c < '0') { |
||||||
|
return Integer.parseInt(str); |
||||||
|
} |
||||||
|
int num = c - '0'; |
||||||
|
if (offset < length) { |
||||||
|
c = str.charAt(offset++); |
||||||
|
if (c > '9' || c < '0') { |
||||||
|
return Integer.parseInt(str); |
||||||
|
} |
||||||
|
num = (num * 10) + (c - '0'); |
||||||
|
if (offset < length) { |
||||||
|
c = str.charAt(offset++); |
||||||
|
if (c > '9' || c < '0') { |
||||||
|
return Integer.parseInt(str); |
||||||
|
} |
||||||
|
num = (num * 10) + (c - '0'); |
||||||
|
// Let's just loop if we have more than 3 digits:
|
||||||
|
if (offset < length) { |
||||||
|
do { |
||||||
|
c = str.charAt(offset++); |
||||||
|
if (c > '9' || c < '0') { |
||||||
|
return Integer.parseInt(str); |
||||||
|
} |
||||||
|
num = (num * 10) + (c - '0'); |
||||||
|
} while (offset < length); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return negative ? -num : num; |
||||||
|
} |
||||||
|
|
||||||
|
public static long parseLong(char[] digitChars, int offset, int len) |
||||||
|
{ |
||||||
|
// Note: caller must ensure length is [10, 18]
|
||||||
|
int len1 = len-9; |
||||||
|
long val = parseInt(digitChars, offset, len1) * L_BILLION; |
||||||
|
return val + (long) parseInt(digitChars, offset+len1, 9); |
||||||
|
} |
||||||
|
|
||||||
|
public static long parseLong(String str) |
||||||
|
{ |
||||||
|
/* Ok, now; as the very first thing, let's just optimize case of "fake longs"; |
||||||
|
* that is, if we know they must be ints, call int parsing |
||||||
|
*/ |
||||||
|
int length = str.length(); |
||||||
|
if (length <= 9) { |
||||||
|
return (long) parseInt(str); |
||||||
|
} |
||||||
|
// !!! TODO: implement efficient 2-int parsing...
|
||||||
|
return Long.parseLong(str); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper method for determining if given String representation of |
||||||
|
* an integral number would fit in 64-bit Java long or not. |
||||||
|
* Note that input String must NOT contain leading minus sign (even |
||||||
|
* if 'negative' is set to true). |
||||||
|
* |
||||||
|
* @param negative Whether original number had a minus sign (which is |
||||||
|
* NOT passed to this method) or not |
||||||
|
*/ |
||||||
|
public static boolean inLongRange(char[] digitChars, int offset, int len, |
||||||
|
boolean negative) |
||||||
|
{ |
||||||
|
String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR; |
||||||
|
int cmpLen = cmpStr.length(); |
||||||
|
if (len < cmpLen) return true; |
||||||
|
if (len > cmpLen) return false; |
||||||
|
|
||||||
|
for (int i = 0; i < cmpLen; ++i) { |
||||||
|
int diff = digitChars[offset+i] - cmpStr.charAt(i); |
||||||
|
if (diff != 0) { |
||||||
|
return (diff < 0); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Similar to {@link #inLongRange(char[],int,int,boolean)}, but |
||||||
|
* with String argument |
||||||
|
* |
||||||
|
* @param negative Whether original number had a minus sign (which is |
||||||
|
* NOT passed to this method) or not |
||||||
|
*/ |
||||||
|
public static boolean inLongRange(String numberStr, boolean negative) |
||||||
|
{ |
||||||
|
String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR; |
||||||
|
int cmpLen = cmpStr.length(); |
||||||
|
int actualLen = numberStr.length(); |
||||||
|
if (actualLen < cmpLen) return true; |
||||||
|
if (actualLen > cmpLen) return false; |
||||||
|
|
||||||
|
// could perhaps just use String.compareTo()?
|
||||||
|
for (int i = 0; i < cmpLen; ++i) { |
||||||
|
int diff = numberStr.charAt(i) - cmpStr.charAt(i); |
||||||
|
if (diff != 0) { |
||||||
|
return (diff < 0); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public static int parseAsInt(String input, int defaultValue) |
||||||
|
{ |
||||||
|
if (input == null) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
input = input.trim(); |
||||||
|
int len = input.length(); |
||||||
|
if (len == 0) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
// One more thing: use integer parsing for 'simple'
|
||||||
|
int i = 0; |
||||||
|
if (i < len) { // skip leading sign:
|
||||||
|
char c = input.charAt(0); |
||||||
|
if (c == '+') { // for plus, actually physically remove
|
||||||
|
input = input.substring(1); |
||||||
|
len = input.length(); |
||||||
|
} else if (c == '-') { // minus, just skip for checks, must retain
|
||||||
|
++i; |
||||||
|
} |
||||||
|
} |
||||||
|
for (; i < len; ++i) { |
||||||
|
char c = input.charAt(i); |
||||||
|
// if other symbols, parse as Double, coerce
|
||||||
|
if (c > '9' || c < '0') { |
||||||
|
try { |
||||||
|
return (int) parseDouble(input); |
||||||
|
} catch (NumberFormatException e) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
try { |
||||||
|
return Integer.parseInt(input); |
||||||
|
} catch (NumberFormatException e) { } |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
public static long parseAsLong(String input, long defaultValue) |
||||||
|
{ |
||||||
|
if (input == null) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
input = input.trim(); |
||||||
|
int len = input.length(); |
||||||
|
if (len == 0) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
// One more thing: use long parsing for 'simple'
|
||||||
|
int i = 0; |
||||||
|
if (i < len) { // skip leading sign:
|
||||||
|
char c = input.charAt(0); |
||||||
|
if (c == '+') { // for plus, actually physically remove
|
||||||
|
input = input.substring(1); |
||||||
|
len = input.length(); |
||||||
|
} else if (c == '-') { // minus, just skip for checks, must retain
|
||||||
|
++i; |
||||||
|
} |
||||||
|
} |
||||||
|
for (; i < len; ++i) { |
||||||
|
char c = input.charAt(i); |
||||||
|
// if other symbols, parse as Double, coerce
|
||||||
|
if (c > '9' || c < '0') { |
||||||
|
try { |
||||||
|
return (long) parseDouble(input); |
||||||
|
} catch (NumberFormatException e) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
try { |
||||||
|
return Long.parseLong(input); |
||||||
|
} catch (NumberFormatException e) { } |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
public static double parseAsDouble(String input, double defaultValue) |
||||||
|
{ |
||||||
|
if (input == null) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
input = input.trim(); |
||||||
|
int len = input.length(); |
||||||
|
if (len == 0) { |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
try { |
||||||
|
return parseDouble(input); |
||||||
|
} catch (NumberFormatException e) { } |
||||||
|
return defaultValue; |
||||||
|
} |
||||||
|
|
||||||
|
public static double parseDouble(String numStr) throws NumberFormatException |
||||||
|
{ |
||||||
|
// [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE?
|
||||||
|
/* as per [JACKSON-827], let's use MIN_VALUE as it is available on all JDKs; normalized |
||||||
|
* only in JDK 1.6. In practice, should not really matter. |
||||||
|
*/ |
||||||
|
if (NASTY_SMALL_DOUBLE.equals(numStr)) { |
||||||
|
return Double.MIN_VALUE; |
||||||
|
} |
||||||
|
return Double.parseDouble(numStr); |
||||||
|
} |
||||||
|
|
||||||
|
public static BigDecimal parseBigDecimal(String numStr) throws NumberFormatException |
||||||
|
{ |
||||||
|
try { |
||||||
|
return new BigDecimal(numStr); |
||||||
|
} catch (NumberFormatException e) { |
||||||
|
throw _badBigDecimal(numStr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static BigDecimal parseBigDecimal(char[] buffer) throws NumberFormatException { |
||||||
|
return parseBigDecimal(buffer, 0, buffer.length); |
||||||
|
} |
||||||
|
|
||||||
|
public static BigDecimal parseBigDecimal(char[] buffer, int offset, int len) |
||||||
|
throws NumberFormatException |
||||||
|
{ |
||||||
|
try { |
||||||
|
return new BigDecimal(buffer, offset, len); |
||||||
|
} catch (NumberFormatException e) { |
||||||
|
throw _badBigDecimal(new String(buffer, offset, len)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static NumberFormatException _badBigDecimal(String str) { |
||||||
|
return new NumberFormatException("Value \""+str+"\" can not be represented as BigDecimal"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,398 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
public final class NumberOutput |
||||||
|
{ |
||||||
|
private final static char NULL_CHAR = (char) 0; |
||||||
|
|
||||||
|
private static int MILLION = 1000000; |
||||||
|
private static int BILLION = 1000000000; |
||||||
|
private static long TEN_BILLION_L = 10000000000L; |
||||||
|
private static long THOUSAND_L = 1000L; |
||||||
|
|
||||||
|
private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE; |
||||||
|
private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE; |
||||||
|
|
||||||
|
final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE); |
||||||
|
|
||||||
|
final static char[] LEADING_TRIPLETS = new char[4000]; |
||||||
|
final static char[] FULL_TRIPLETS = new char[4000]; |
||||||
|
static { |
||||||
|
/* Let's fill it with NULLs for ignorable leading digits, |
||||||
|
* and digit chars for others |
||||||
|
*/ |
||||||
|
int ix = 0; |
||||||
|
for (int i1 = 0; i1 < 10; ++i1) { |
||||||
|
char f1 = (char) ('0' + i1); |
||||||
|
char l1 = (i1 == 0) ? NULL_CHAR : f1; |
||||||
|
for (int i2 = 0; i2 < 10; ++i2) { |
||||||
|
char f2 = (char) ('0' + i2); |
||||||
|
char l2 = (i1 == 0 && i2 == 0) ? NULL_CHAR : f2; |
||||||
|
for (int i3 = 0; i3 < 10; ++i3) { |
||||||
|
// Last is never to be empty
|
||||||
|
char f3 = (char) ('0' + i3); |
||||||
|
LEADING_TRIPLETS[ix] = l1; |
||||||
|
LEADING_TRIPLETS[ix+1] = l2; |
||||||
|
LEADING_TRIPLETS[ix+2] = f3; |
||||||
|
FULL_TRIPLETS[ix] = f1; |
||||||
|
FULL_TRIPLETS[ix+1] = f2; |
||||||
|
FULL_TRIPLETS[ix+2] = f3; |
||||||
|
ix += 4; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final static byte[] FULL_TRIPLETS_B = new byte[4000]; |
||||||
|
static { |
||||||
|
for (int i = 0; i < 4000; ++i) { |
||||||
|
FULL_TRIPLETS_B[i] = (byte) FULL_TRIPLETS[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final static String[] sSmallIntStrs = new String[] { |
||||||
|
"0","1","2","3","4","5","6","7","8","9","10" |
||||||
|
}; |
||||||
|
final static String[] sSmallIntStrs2 = new String[] { |
||||||
|
"-1","-2","-3","-4","-5","-6","-7","-8","-9","-10" |
||||||
|
}; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Efficient serialization methods using raw buffers |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Offset within buffer after outputting int |
||||||
|
*/ |
||||||
|
public static int outputInt(int value, char[] buffer, int offset) |
||||||
|
{ |
||||||
|
if (value < 0) { |
||||||
|
if (value == Integer.MIN_VALUE) { |
||||||
|
/* Special case: no matching positive value within range; |
||||||
|
* let's then "upgrade" to long and output as such. |
||||||
|
*/ |
||||||
|
return outputLong((long) value, buffer, offset); |
||||||
|
} |
||||||
|
buffer[offset++] = '-'; |
||||||
|
value = -value; |
||||||
|
} |
||||||
|
|
||||||
|
if (value < MILLION) { // at most 2 triplets...
|
||||||
|
if (value < 1000) { |
||||||
|
if (value < 10) { |
||||||
|
buffer[offset++] = (char) ('0' + value); |
||||||
|
} else { |
||||||
|
offset = outputLeadingTriplet(value, buffer, offset); |
||||||
|
} |
||||||
|
} else { |
||||||
|
int thousands = value / 1000; |
||||||
|
value -= (thousands * 1000); // == value % 1000
|
||||||
|
offset = outputLeadingTriplet(thousands, buffer, offset); |
||||||
|
offset = outputFullTriplet(value, buffer, offset); |
||||||
|
} |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
// ok, all 3 triplets included
|
||||||
|
/* Let's first hand possible billions separately before |
||||||
|
* handling 3 triplets. This is possible since we know we |
||||||
|
* can have at most '2' as billion count. |
||||||
|
*/ |
||||||
|
boolean hasBillions = (value >= BILLION); |
||||||
|
if (hasBillions) { |
||||||
|
value -= BILLION; |
||||||
|
if (value >= BILLION) { |
||||||
|
value -= BILLION; |
||||||
|
buffer[offset++] = '2'; |
||||||
|
} else { |
||||||
|
buffer[offset++] = '1'; |
||||||
|
} |
||||||
|
} |
||||||
|
int newValue = value / 1000; |
||||||
|
int ones = (value - (newValue * 1000)); // == value % 1000
|
||||||
|
value = newValue; |
||||||
|
newValue /= 1000; |
||||||
|
int thousands = (value - (newValue * 1000)); |
||||||
|
|
||||||
|
// value now has millions, which have 1, 2 or 3 digits
|
||||||
|
if (hasBillions) { |
||||||
|
offset = outputFullTriplet(newValue, buffer, offset); |
||||||
|
} else { |
||||||
|
offset = outputLeadingTriplet(newValue, buffer, offset); |
||||||
|
} |
||||||
|
offset = outputFullTriplet(thousands, buffer, offset); |
||||||
|
offset = outputFullTriplet(ones, buffer, offset); |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
public static int outputInt(int value, byte[] buffer, int offset) |
||||||
|
{ |
||||||
|
if (value < 0) { |
||||||
|
if (value == Integer.MIN_VALUE) { |
||||||
|
return outputLong((long) value, buffer, offset); |
||||||
|
} |
||||||
|
buffer[offset++] = '-'; |
||||||
|
value = -value; |
||||||
|
} |
||||||
|
|
||||||
|
if (value < MILLION) { // at most 2 triplets...
|
||||||
|
if (value < 1000) { |
||||||
|
if (value < 10) { |
||||||
|
buffer[offset++] = (byte) ('0' + value); |
||||||
|
} else { |
||||||
|
offset = outputLeadingTriplet(value, buffer, offset); |
||||||
|
} |
||||||
|
} else { |
||||||
|
int thousands = value / 1000; |
||||||
|
value -= (thousands * 1000); // == value % 1000
|
||||||
|
offset = outputLeadingTriplet(thousands, buffer, offset); |
||||||
|
offset = outputFullTriplet(value, buffer, offset); |
||||||
|
} |
||||||
|
return offset; |
||||||
|
} |
||||||
|
boolean hasBillions = (value >= BILLION); |
||||||
|
if (hasBillions) { |
||||||
|
value -= BILLION; |
||||||
|
if (value >= BILLION) { |
||||||
|
value -= BILLION; |
||||||
|
buffer[offset++] = '2'; |
||||||
|
} else { |
||||||
|
buffer[offset++] = '1'; |
||||||
|
} |
||||||
|
} |
||||||
|
int newValue = value / 1000; |
||||||
|
int ones = (value - (newValue * 1000)); // == value % 1000
|
||||||
|
value = newValue; |
||||||
|
newValue /= 1000; |
||||||
|
int thousands = (value - (newValue * 1000)); |
||||||
|
|
||||||
|
if (hasBillions) { |
||||||
|
offset = outputFullTriplet(newValue, buffer, offset); |
||||||
|
} else { |
||||||
|
offset = outputLeadingTriplet(newValue, buffer, offset); |
||||||
|
} |
||||||
|
offset = outputFullTriplet(thousands, buffer, offset); |
||||||
|
offset = outputFullTriplet(ones, buffer, offset); |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Offset within buffer after outputting int |
||||||
|
*/ |
||||||
|
public static int outputLong(long value, char[] buffer, int offset) |
||||||
|
{ |
||||||
|
// First: does it actually fit in an int?
|
||||||
|
if (value < 0L) { |
||||||
|
/* MIN_INT is actually printed as long, just because its |
||||||
|
* negation is not an int but long |
||||||
|
*/ |
||||||
|
if (value > MIN_INT_AS_LONG) { |
||||||
|
return outputInt((int) value, buffer, offset); |
||||||
|
} |
||||||
|
if (value == Long.MIN_VALUE) { |
||||||
|
// Special case: no matching positive value within range
|
||||||
|
int len = SMALLEST_LONG.length(); |
||||||
|
SMALLEST_LONG.getChars(0, len, buffer, offset); |
||||||
|
return (offset + len); |
||||||
|
} |
||||||
|
buffer[offset++] = '-'; |
||||||
|
value = -value; |
||||||
|
} else { |
||||||
|
if (value <= MAX_INT_AS_LONG) { |
||||||
|
return outputInt((int) value, buffer, offset); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Ok: real long print. Need to first figure out length |
||||||
|
* in characters, and then print in from end to beginning |
||||||
|
*/ |
||||||
|
int origOffset = offset; |
||||||
|
offset += calcLongStrLength(value); |
||||||
|
int ptr = offset; |
||||||
|
|
||||||
|
// First, with long arithmetics:
|
||||||
|
while (value > MAX_INT_AS_LONG) { // full triplet
|
||||||
|
ptr -= 3; |
||||||
|
long newValue = value / THOUSAND_L; |
||||||
|
int triplet = (int) (value - newValue * THOUSAND_L); |
||||||
|
outputFullTriplet(triplet, buffer, ptr); |
||||||
|
value = newValue; |
||||||
|
} |
||||||
|
// Then with int arithmetics:
|
||||||
|
int ivalue = (int) value; |
||||||
|
while (ivalue >= 1000) { // still full triplet
|
||||||
|
ptr -= 3; |
||||||
|
int newValue = ivalue / 1000; |
||||||
|
int triplet = ivalue - (newValue * 1000); |
||||||
|
outputFullTriplet(triplet, buffer, ptr); |
||||||
|
ivalue = newValue; |
||||||
|
} |
||||||
|
// And finally, if anything remains, partial triplet
|
||||||
|
outputLeadingTriplet(ivalue, buffer, origOffset); |
||||||
|
|
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
public static int outputLong(long value, byte[] buffer, int offset) |
||||||
|
{ |
||||||
|
if (value < 0L) { |
||||||
|
if (value > MIN_INT_AS_LONG) { |
||||||
|
return outputInt((int) value, buffer, offset); |
||||||
|
} |
||||||
|
if (value == Long.MIN_VALUE) { |
||||||
|
// Special case: no matching positive value within range
|
||||||
|
int len = SMALLEST_LONG.length(); |
||||||
|
for (int i = 0; i < len; ++i) { |
||||||
|
buffer[offset++] = (byte) SMALLEST_LONG.charAt(i); |
||||||
|
} |
||||||
|
return offset; |
||||||
|
} |
||||||
|
buffer[offset++] = '-'; |
||||||
|
value = -value; |
||||||
|
} else { |
||||||
|
if (value <= MAX_INT_AS_LONG) { |
||||||
|
return outputInt((int) value, buffer, offset); |
||||||
|
} |
||||||
|
} |
||||||
|
int origOffset = offset; |
||||||
|
offset += calcLongStrLength(value); |
||||||
|
int ptr = offset; |
||||||
|
|
||||||
|
// First, with long arithmetics:
|
||||||
|
while (value > MAX_INT_AS_LONG) { // full triplet
|
||||||
|
ptr -= 3; |
||||||
|
long newValue = value / THOUSAND_L; |
||||||
|
int triplet = (int) (value - newValue * THOUSAND_L); |
||||||
|
outputFullTriplet(triplet, buffer, ptr); |
||||||
|
value = newValue; |
||||||
|
} |
||||||
|
// Then with int arithmetics:
|
||||||
|
int ivalue = (int) value; |
||||||
|
while (ivalue >= 1000) { // still full triplet
|
||||||
|
ptr -= 3; |
||||||
|
int newValue = ivalue / 1000; |
||||||
|
int triplet = ivalue - (newValue * 1000); |
||||||
|
outputFullTriplet(triplet, buffer, ptr); |
||||||
|
ivalue = newValue; |
||||||
|
} |
||||||
|
outputLeadingTriplet(ivalue, buffer, origOffset); |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Secondary convenience serialization methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/* !!! 05-Aug-2008, tatus: Any ways to further optimize |
||||||
|
* these? (or need: only called by diagnostics methods?) |
||||||
|
*/ |
||||||
|
|
||||||
|
public static String toString(int value) |
||||||
|
{ |
||||||
|
// Lookup table for small values
|
||||||
|
if (value < sSmallIntStrs.length) { |
||||||
|
if (value >= 0) { |
||||||
|
return sSmallIntStrs[value]; |
||||||
|
} |
||||||
|
int v2 = -value - 1; |
||||||
|
if (v2 < sSmallIntStrs2.length) { |
||||||
|
return sSmallIntStrs2[v2]; |
||||||
|
} |
||||||
|
} |
||||||
|
return Integer.toString(value); |
||||||
|
} |
||||||
|
|
||||||
|
public static String toString(long value) |
||||||
|
{ |
||||||
|
if (value <= Integer.MAX_VALUE && |
||||||
|
value >= Integer.MIN_VALUE) { |
||||||
|
return toString((int) value); |
||||||
|
} |
||||||
|
return Long.toString(value); |
||||||
|
} |
||||||
|
|
||||||
|
public static String toString(double value) |
||||||
|
{ |
||||||
|
return Double.toString(value); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
private static int outputLeadingTriplet(int triplet, char[] buffer, int offset) |
||||||
|
{ |
||||||
|
int digitOffset = (triplet << 2); |
||||||
|
char c = LEADING_TRIPLETS[digitOffset++]; |
||||||
|
if (c != NULL_CHAR) { |
||||||
|
buffer[offset++] = c; |
||||||
|
} |
||||||
|
c = LEADING_TRIPLETS[digitOffset++]; |
||||||
|
if (c != NULL_CHAR) { |
||||||
|
buffer[offset++] = c; |
||||||
|
} |
||||||
|
// Last is required to be non-empty
|
||||||
|
buffer[offset++] = LEADING_TRIPLETS[digitOffset]; |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
private static int outputLeadingTriplet(int triplet, byte[] buffer, int offset) |
||||||
|
{ |
||||||
|
int digitOffset = (triplet << 2); |
||||||
|
char c = LEADING_TRIPLETS[digitOffset++]; |
||||||
|
if (c != NULL_CHAR) { |
||||||
|
buffer[offset++] = (byte) c; |
||||||
|
} |
||||||
|
c = LEADING_TRIPLETS[digitOffset++]; |
||||||
|
if (c != NULL_CHAR) { |
||||||
|
buffer[offset++] = (byte) c; |
||||||
|
} |
||||||
|
// Last is required to be non-empty
|
||||||
|
buffer[offset++] = (byte) LEADING_TRIPLETS[digitOffset]; |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
private static int outputFullTriplet(int triplet, char[] buffer, int offset) |
||||||
|
{ |
||||||
|
int digitOffset = (triplet << 2); |
||||||
|
buffer[offset++] = FULL_TRIPLETS[digitOffset++]; |
||||||
|
buffer[offset++] = FULL_TRIPLETS[digitOffset++]; |
||||||
|
buffer[offset++] = FULL_TRIPLETS[digitOffset]; |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
private static int outputFullTriplet(int triplet, byte[] buffer, int offset) |
||||||
|
{ |
||||||
|
int digitOffset = (triplet << 2); |
||||||
|
buffer[offset++] = FULL_TRIPLETS_B[digitOffset++]; |
||||||
|
buffer[offset++] = FULL_TRIPLETS_B[digitOffset++]; |
||||||
|
buffer[offset++] = FULL_TRIPLETS_B[digitOffset]; |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
*<p> |
||||||
|
* Pre-conditions: posValue is positive, and larger than |
||||||
|
* Integer.MAX_VALUE (about 2 billions). |
||||||
|
*/ |
||||||
|
private static int calcLongStrLength(long posValue) |
||||||
|
{ |
||||||
|
int len = 10; |
||||||
|
long comp = TEN_BILLION_L; |
||||||
|
|
||||||
|
// 19 is longest, need to worry about overflow
|
||||||
|
while (posValue >= comp) { |
||||||
|
if (len == 19) { |
||||||
|
break; |
||||||
|
} |
||||||
|
++len; |
||||||
|
comp = (comp << 3) + (comp << 1); // 10x
|
||||||
|
} |
||||||
|
return len; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Handler class that can be used to decorate output destinations. |
||||||
|
* Typical use is to use a filter abstraction (filtered output stream, |
||||||
|
* writer) around original output destination, and apply additional |
||||||
|
* processing during write operations. |
||||||
|
*/ |
||||||
|
public abstract class OutputDecorator |
||||||
|
implements java.io.Serializable // since 2.1
|
||||||
|
{ |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called by {@link com.fr.third.fasterxml.jackson.core.JsonFactory} instance when |
||||||
|
* creating generator for given {@link OutputStream}, when this decorator |
||||||
|
* has been registered. |
||||||
|
* |
||||||
|
* @param ctxt IO context in use (provides access to declared encoding) |
||||||
|
* @param out Original output destination |
||||||
|
* |
||||||
|
* @return OutputStream to use; either passed in argument, or something that |
||||||
|
* calls it |
||||||
|
*/ |
||||||
|
public abstract OutputStream decorate(IOContext ctxt, OutputStream out) |
||||||
|
throws IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called by {@link com.fr.third.fasterxml.jackson.core.JsonFactory} instance when |
||||||
|
* creating generator for given {@link Writer}, when this decorator |
||||||
|
* has been registered. |
||||||
|
* |
||||||
|
* @param ctxt IO context in use (provides access to declared encoding) |
||||||
|
* @param w Original output writer |
||||||
|
* |
||||||
|
* @return Writer to use; either passed in argument, or something that calls it |
||||||
|
*/ |
||||||
|
public abstract Writer decorate(IOContext ctxt, Writer w) throws IOException; |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.util.BufferRecycler; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.TextBuffer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Efficient alternative to {@link StringWriter}, based on using segmented |
||||||
|
* internal buffer. Initial input buffer is also recyclable. |
||||||
|
*<p> |
||||||
|
* This class is most useful when serializing JSON content as a String: |
||||||
|
* if so, instance of this class can be given as the writer to |
||||||
|
* <code>JsonGenerator</code>. |
||||||
|
*/ |
||||||
|
public final class SegmentedStringWriter |
||||||
|
extends Writer |
||||||
|
{ |
||||||
|
final protected TextBuffer _buffer; |
||||||
|
|
||||||
|
public SegmentedStringWriter(BufferRecycler br) |
||||||
|
{ |
||||||
|
super(); |
||||||
|
_buffer = new TextBuffer(br); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* java.io.Writer implementation |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public Writer append(char c) |
||||||
|
{ |
||||||
|
write(c); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Writer append(CharSequence csq) |
||||||
|
{ |
||||||
|
String str = csq.toString(); |
||||||
|
_buffer.append(str, 0, str.length()); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Writer append(CharSequence csq, int start, int end) |
||||||
|
{ |
||||||
|
String str = csq.subSequence(start, end).toString(); |
||||||
|
_buffer.append(str, 0, str.length()); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void close() { } // NOP
|
||||||
|
|
||||||
|
@Override public void flush() { } // NOP
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(char[] cbuf) { |
||||||
|
_buffer.append(cbuf, 0, cbuf.length); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(char[] cbuf, int off, int len) { |
||||||
|
_buffer.append(cbuf, off, len); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(int c) { |
||||||
|
_buffer.append((char) c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String str) { _buffer.append(str, 0, str.length()); } |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String str, int off, int len) { |
||||||
|
_buffer.append(str, off, len); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Extended API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Main access method that will construct a String that contains |
||||||
|
* all the contents, release all internal buffers we may have, |
||||||
|
* and return result String. |
||||||
|
* Note that the method is not idempotent -- if called second time, |
||||||
|
* will just return an empty String. |
||||||
|
*/ |
||||||
|
public String getAndClear() |
||||||
|
{ |
||||||
|
String result = _buffer.contentsAsString(); |
||||||
|
_buffer.releaseBuffers(); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,280 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.SerializableString; |
||||||
|
|
||||||
|
/** |
||||||
|
* String token that can lazily serialize String contained and then reuse that |
||||||
|
* serialization later on. This is similar to JDBC prepared statements, for example, |
||||||
|
* in that instances should only be created when they are used more than use; |
||||||
|
* prime candidates are various serializers. |
||||||
|
*<p> |
||||||
|
* Class is final for performance reasons and since this is not designed to |
||||||
|
* be extensible or customizable (customizations would occur in calling code) |
||||||
|
*/ |
||||||
|
public class SerializedString |
||||||
|
implements SerializableString, java.io.Serializable |
||||||
|
{ |
||||||
|
protected final String _value; |
||||||
|
|
||||||
|
/* 13-Dec-2010, tatu: Whether use volatile or not is actually an important |
||||||
|
* decision for multi-core use cases. Cost of volatility can be non-trivial |
||||||
|
* for heavy use cases, and serialized-string instances are accessed often. |
||||||
|
* Given that all code paths with common Jackson usage patterns go through |
||||||
|
* a few memory barriers (mostly with cache/reuse pool access) it seems safe |
||||||
|
* enough to omit volatiles here, given how simple lazy initialization is. |
||||||
|
* This can be compared to how {@link String#intern} works; lazily and |
||||||
|
* without synchronization or use of volatile keyword. |
||||||
|
* |
||||||
|
* Change to remove volatile was a request by implementors of a high-throughput |
||||||
|
* search framework; and they believed this is an important optimization for |
||||||
|
* heaviest, multi-core deployed use cases. |
||||||
|
*/ |
||||||
|
/* |
||||||
|
* 22-Sep-2013, tatu: FWIW, there have been no reports of problems in this |
||||||
|
* area, or anything pointing to it. So I think we are safe up to JDK7 |
||||||
|
*/ |
||||||
|
|
||||||
|
protected /*volatile*/ byte[] _quotedUTF8Ref; |
||||||
|
|
||||||
|
protected /*volatile*/ byte[] _unquotedUTF8Ref; |
||||||
|
|
||||||
|
protected /*volatile*/ char[] _quotedChars; |
||||||
|
|
||||||
|
public SerializedString(String v) { |
||||||
|
if (v == null) { |
||||||
|
throw new IllegalStateException("Null String illegal for SerializedString"); |
||||||
|
} |
||||||
|
_value = v; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Serializable overrides |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Ugly hack, to work through the requirement that _value is indeed final, |
||||||
|
* and that JDK serialization won't call ctor(s). |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
protected transient String _jdkSerializeValue; |
||||||
|
|
||||||
|
private void readObject(ObjectInputStream in) throws IOException { |
||||||
|
_jdkSerializeValue = in.readUTF(); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeObject(ObjectOutputStream out) throws IOException { |
||||||
|
out.writeUTF(_value); |
||||||
|
} |
||||||
|
|
||||||
|
protected Object readResolve() { |
||||||
|
return new SerializedString(_jdkSerializeValue); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public final String getValue() { return _value; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns length of the String as characters |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public final int charLength() { return _value.length(); } |
||||||
|
|
||||||
|
@Override |
||||||
|
public final char[] asQuotedChars() |
||||||
|
{ |
||||||
|
char[] result = _quotedChars; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().quoteAsString(_value); |
||||||
|
_quotedChars = result; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for accessing value that has been quoted using JSON |
||||||
|
* quoting rules, and encoded using UTF-8 encoding. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public final byte[] asUnquotedUTF8() |
||||||
|
{ |
||||||
|
byte[] result = _unquotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); |
||||||
|
_unquotedUTF8Ref = result; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Accessor for accessing value as is (without JSON quoting) |
||||||
|
* encoded using UTF-8 encoding. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public final byte[] asQuotedUTF8() |
||||||
|
{ |
||||||
|
byte[] result = _quotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); |
||||||
|
_quotedUTF8Ref = result; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Additional 2.0 methods for appending/writing contents |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public int appendQuotedUTF8(byte[] buffer, int offset) |
||||||
|
{ |
||||||
|
byte[] result = _quotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); |
||||||
|
_quotedUTF8Ref = result; |
||||||
|
} |
||||||
|
final int length = result.length; |
||||||
|
if ((offset + length) > buffer.length) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
System.arraycopy(result, 0, buffer, offset, length); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int appendQuoted(char[] buffer, int offset) |
||||||
|
{ |
||||||
|
char[] result = _quotedChars; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().quoteAsString(_value); |
||||||
|
_quotedChars = result; |
||||||
|
} |
||||||
|
final int length = result.length; |
||||||
|
if ((offset + length) > buffer.length) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
System.arraycopy(result, 0, buffer, offset, length); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int appendUnquotedUTF8(byte[] buffer, int offset) |
||||||
|
{ |
||||||
|
byte[] result = _unquotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); |
||||||
|
_unquotedUTF8Ref = result; |
||||||
|
} |
||||||
|
final int length = result.length; |
||||||
|
if ((offset + length) > buffer.length) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
System.arraycopy(result, 0, buffer, offset, length); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int appendUnquoted(char[] buffer, int offset) |
||||||
|
{ |
||||||
|
String str = _value; |
||||||
|
final int length = str.length(); |
||||||
|
if ((offset + length) > buffer.length) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
str.getChars(0, length, buffer, offset); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int writeQuotedUTF8(OutputStream out) throws IOException |
||||||
|
{ |
||||||
|
byte[] result = _quotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); |
||||||
|
_quotedUTF8Ref = result; |
||||||
|
} |
||||||
|
final int length = result.length; |
||||||
|
out.write(result, 0, length); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int writeUnquotedUTF8(OutputStream out) throws IOException |
||||||
|
{ |
||||||
|
byte[] result = _unquotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); |
||||||
|
_unquotedUTF8Ref = result; |
||||||
|
} |
||||||
|
final int length = result.length; |
||||||
|
out.write(result, 0, length); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int putQuotedUTF8(ByteBuffer buffer) |
||||||
|
{ |
||||||
|
byte[] result = _quotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); |
||||||
|
_quotedUTF8Ref = result; |
||||||
|
} |
||||||
|
final int length = result.length; |
||||||
|
if (length > buffer.remaining()) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
buffer.put(result, 0, length); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int putUnquotedUTF8(ByteBuffer buffer) |
||||||
|
{ |
||||||
|
byte[] result = _unquotedUTF8Ref; |
||||||
|
if (result == null) { |
||||||
|
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); |
||||||
|
_unquotedUTF8Ref = result; |
||||||
|
} |
||||||
|
final int length = result.length; |
||||||
|
if (length > buffer.remaining()) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
buffer.put(result, 0, length); |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Standard method overrides |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public final String toString() { return _value; } |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int hashCode() { return _value.hashCode(); } |
||||||
|
|
||||||
|
@Override |
||||||
|
public final boolean equals(Object o) |
||||||
|
{ |
||||||
|
if (o == this) return true; |
||||||
|
if (o == null || o.getClass() != getClass()) return false; |
||||||
|
SerializedString other = (SerializedString) o; |
||||||
|
return _value.equals(other._value); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,218 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Since JDK does not come with UTF-32/UCS-4, let's implement a simple |
||||||
|
* decoder to use. |
||||||
|
*/ |
||||||
|
public class UTF32Reader |
||||||
|
extends BaseReader |
||||||
|
{ |
||||||
|
protected final boolean _bigEndian; |
||||||
|
|
||||||
|
/** |
||||||
|
* Although input is fine with full Unicode set, Java still uses |
||||||
|
* 16-bit chars, so we may have to split high-order chars into |
||||||
|
* surrogate pairs. |
||||||
|
*/ |
||||||
|
protected char _surrogate = NULL_CHAR; |
||||||
|
|
||||||
|
/** |
||||||
|
* Total read character count; used for error reporting purposes |
||||||
|
*/ |
||||||
|
protected int _charCount = 0; |
||||||
|
|
||||||
|
/** |
||||||
|
* Total read byte count; used for error reporting purposes |
||||||
|
*/ |
||||||
|
protected int _byteCount = 0; |
||||||
|
|
||||||
|
protected final boolean _managedBuffers; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public UTF32Reader(IOContext ctxt, |
||||||
|
InputStream in, byte[] buf, int ptr, int len, |
||||||
|
boolean isBigEndian) |
||||||
|
{ |
||||||
|
super(ctxt, in, buf, ptr, len); |
||||||
|
_bigEndian = isBigEndian; |
||||||
|
_managedBuffers = (in != null); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public int read(char[] cbuf, int start, int len) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
// Already EOF?
|
||||||
|
if (_buffer == null) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
if (len < 1) { |
||||||
|
return len; |
||||||
|
} |
||||||
|
// Let's then ensure there's enough room...
|
||||||
|
if (start < 0 || (start+len) > cbuf.length) { |
||||||
|
reportBounds(cbuf, start, len); |
||||||
|
} |
||||||
|
|
||||||
|
len += start; |
||||||
|
int outPtr = start; |
||||||
|
|
||||||
|
// Ok, first; do we have a surrogate from last round?
|
||||||
|
if (_surrogate != NULL_CHAR) { |
||||||
|
cbuf[outPtr++] = _surrogate; |
||||||
|
_surrogate = NULL_CHAR; |
||||||
|
// No need to load more, already got one char
|
||||||
|
} else { |
||||||
|
/* Note: we'll try to avoid blocking as much as possible. As a |
||||||
|
* result, we only need to get 4 bytes for a full char. |
||||||
|
*/ |
||||||
|
int left = (_length - _ptr); |
||||||
|
if (left < 4) { |
||||||
|
if (!loadMore(left)) { // (legal) EOF?
|
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
main_loop: |
||||||
|
while (outPtr < len) { |
||||||
|
int ptr = _ptr; |
||||||
|
int ch; |
||||||
|
|
||||||
|
if (_bigEndian) { |
||||||
|
ch = (_buffer[ptr] << 24) | ((_buffer[ptr+1] & 0xFF) << 16) |
||||||
|
| ((_buffer[ptr+2] & 0xFF) << 8) | (_buffer[ptr+3] & 0xFF); |
||||||
|
} else { |
||||||
|
ch = (_buffer[ptr] & 0xFF) | ((_buffer[ptr+1] & 0xFF) << 8) |
||||||
|
| ((_buffer[ptr+2] & 0xFF) << 16) | (_buffer[ptr+3] << 24); |
||||||
|
} |
||||||
|
_ptr += 4; |
||||||
|
|
||||||
|
// Does it need to be split to surrogates?
|
||||||
|
// (also, we can and need to verify illegal chars)
|
||||||
|
if (ch > 0xFFFF) { // need to split into surrogates?
|
||||||
|
if (ch > LAST_VALID_UNICODE_CHAR) { |
||||||
|
reportInvalid(ch, outPtr-start, |
||||||
|
"(above "+Integer.toHexString(LAST_VALID_UNICODE_CHAR)+") "); |
||||||
|
} |
||||||
|
ch -= 0x10000; // to normalize it starting with 0x0
|
||||||
|
cbuf[outPtr++] = (char) (0xD800 + (ch >> 10)); |
||||||
|
// hmmh. can this ever be 0? (not legal, at least?)
|
||||||
|
ch = (0xDC00 | (ch & 0x03FF)); |
||||||
|
// Room for second part?
|
||||||
|
if (outPtr >= len) { // nope
|
||||||
|
_surrogate = (char) ch; |
||||||
|
break main_loop; |
||||||
|
} |
||||||
|
} |
||||||
|
cbuf[outPtr++] = (char) ch; |
||||||
|
if (_ptr >= _length) { |
||||||
|
break main_loop; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
len = outPtr - start; |
||||||
|
_charCount += len; |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
private void reportUnexpectedEOF(int gotBytes, int needed) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
int bytePos = _byteCount + gotBytes; |
||||||
|
int charPos = _charCount; |
||||||
|
|
||||||
|
throw new CharConversionException("Unexpected EOF in the middle of a 4-byte UTF-32 char: got " |
||||||
|
+gotBytes+", needed "+needed+", at char #"+charPos+", byte #"+bytePos+")"); |
||||||
|
} |
||||||
|
|
||||||
|
private void reportInvalid(int value, int offset, String msg) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
int bytePos = _byteCount + _ptr - 1; |
||||||
|
int charPos = _charCount + offset; |
||||||
|
|
||||||
|
throw new CharConversionException("Invalid UTF-32 character 0x" |
||||||
|
+Integer.toHexString(value)+msg+" at char #"+charPos+", byte #"+bytePos+")"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param available Number of "unused" bytes in the input buffer |
||||||
|
* |
||||||
|
* @return True, if enough bytes were read to allow decoding of at least |
||||||
|
* one full character; false if EOF was encountered instead. |
||||||
|
*/ |
||||||
|
private boolean loadMore(int available) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
_byteCount += (_length - available); |
||||||
|
|
||||||
|
// Bytes that need to be moved to the beginning of buffer?
|
||||||
|
if (available > 0) { |
||||||
|
if (_ptr > 0) { |
||||||
|
for (int i = 0; i < available; ++i) { |
||||||
|
_buffer[i] = _buffer[_ptr+i]; |
||||||
|
} |
||||||
|
_ptr = 0; |
||||||
|
} |
||||||
|
_length = available; |
||||||
|
} else { |
||||||
|
/* Ok; here we can actually reasonably expect an EOF, |
||||||
|
* so let's do a separate read right away: |
||||||
|
*/ |
||||||
|
_ptr = 0; |
||||||
|
int count = (_in == null) ? -1 : _in.read(_buffer); |
||||||
|
if (count < 1) { |
||||||
|
_length = 0; |
||||||
|
if (count < 0) { // -1
|
||||||
|
if (_managedBuffers) { |
||||||
|
freeBuffers(); // to help GC?
|
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
// 0 count is no good; let's err out
|
||||||
|
reportStrangeStream(); |
||||||
|
} |
||||||
|
_length = count; |
||||||
|
} |
||||||
|
|
||||||
|
/* Need at least 4 bytes; if we don't get that many, it's an |
||||||
|
* error. |
||||||
|
*/ |
||||||
|
while (_length < 4) { |
||||||
|
int count = (_in == null) ? -1 : _in.read(_buffer, _length, _buffer.length - _length); |
||||||
|
if (count < 1) { |
||||||
|
if (count < 0) { // -1, EOF... no good!
|
||||||
|
if (_managedBuffers) { |
||||||
|
freeBuffers(); // to help GC?
|
||||||
|
} |
||||||
|
reportUnexpectedEOF(_length, 4); |
||||||
|
} |
||||||
|
// 0 count is no good; let's err out
|
||||||
|
reportStrangeStream(); |
||||||
|
} |
||||||
|
_length += count; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,387 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.io; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
public final class UTF8Writer extends Writer |
||||||
|
{ |
||||||
|
final static int SURR1_FIRST = 0xD800; |
||||||
|
final static int SURR1_LAST = 0xDBFF; |
||||||
|
final static int SURR2_FIRST = 0xDC00; |
||||||
|
final static int SURR2_LAST = 0xDFFF; |
||||||
|
|
||||||
|
final private IOContext _context; |
||||||
|
|
||||||
|
private OutputStream _out; |
||||||
|
|
||||||
|
private byte[] _outBuffer; |
||||||
|
|
||||||
|
final private int _outBufferEnd; |
||||||
|
|
||||||
|
private int _outPtr; |
||||||
|
|
||||||
|
/** |
||||||
|
* When outputting chars from BMP, surrogate pairs need to be coalesced. |
||||||
|
* To do this, both pairs must be known first; and since it is possible |
||||||
|
* pairs may be split, we need temporary storage for the first half |
||||||
|
*/ |
||||||
|
private int _surrogate = 0; |
||||||
|
|
||||||
|
public UTF8Writer(IOContext ctxt, OutputStream out) |
||||||
|
{ |
||||||
|
_context = ctxt; |
||||||
|
_out = out; |
||||||
|
|
||||||
|
_outBuffer = ctxt.allocWriteEncodingBuffer(); |
||||||
|
/* Max. expansion for a single char (in unmodified UTF-8) is |
||||||
|
* 4 bytes (or 3 depending on how you view it -- 4 when recombining |
||||||
|
* surrogate pairs) |
||||||
|
*/ |
||||||
|
_outBufferEnd = _outBuffer.length - 4; |
||||||
|
_outPtr = 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Writer append(char c) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
write(c); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
if (_out != null) { |
||||||
|
if (_outPtr > 0) { |
||||||
|
_out.write(_outBuffer, 0, _outPtr); |
||||||
|
_outPtr = 0; |
||||||
|
} |
||||||
|
OutputStream out = _out; |
||||||
|
_out = null; |
||||||
|
|
||||||
|
byte[] buf = _outBuffer; |
||||||
|
if (buf != null) { |
||||||
|
_outBuffer = null; |
||||||
|
_context.releaseWriteEncodingBuffer(buf); |
||||||
|
} |
||||||
|
|
||||||
|
out.close(); |
||||||
|
|
||||||
|
/* Let's 'flush' orphan surrogate, no matter what; but only |
||||||
|
* after cleanly closing everything else. |
||||||
|
*/ |
||||||
|
int code = _surrogate; |
||||||
|
_surrogate = 0; |
||||||
|
if (code > 0) { |
||||||
|
illegalSurrogate(code); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void flush() |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
if (_out != null) { |
||||||
|
if (_outPtr > 0) { |
||||||
|
_out.write(_outBuffer, 0, _outPtr); |
||||||
|
_outPtr = 0; |
||||||
|
} |
||||||
|
_out.flush(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(char[] cbuf) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
write(cbuf, 0, cbuf.length); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(char[] cbuf, int off, int len) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
if (len < 2) { |
||||||
|
if (len == 1) { |
||||||
|
write(cbuf[off]); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// First: do we have a leftover surrogate to deal with?
|
||||||
|
if (_surrogate > 0) { |
||||||
|
char second = cbuf[off++]; |
||||||
|
--len; |
||||||
|
write(convertSurrogate(second)); |
||||||
|
// will have at least one more char
|
||||||
|
} |
||||||
|
|
||||||
|
int outPtr = _outPtr; |
||||||
|
byte[] outBuf = _outBuffer; |
||||||
|
int outBufLast = _outBufferEnd; // has 4 'spare' bytes
|
||||||
|
|
||||||
|
// All right; can just loop it nice and easy now:
|
||||||
|
len += off; // len will now be the end of input buffer
|
||||||
|
|
||||||
|
output_loop: |
||||||
|
for (; off < len; ) { |
||||||
|
/* First, let's ensure we can output at least 4 bytes |
||||||
|
* (longest UTF-8 encoded codepoint): |
||||||
|
*/ |
||||||
|
if (outPtr >= outBufLast) { |
||||||
|
_out.write(outBuf, 0, outPtr); |
||||||
|
outPtr = 0; |
||||||
|
} |
||||||
|
|
||||||
|
int c = cbuf[off++]; |
||||||
|
// And then see if we have an Ascii char:
|
||||||
|
if (c < 0x80) { // If so, can do a tight inner loop:
|
||||||
|
outBuf[outPtr++] = (byte)c; |
||||||
|
// Let's calc how many ascii chars we can copy at most:
|
||||||
|
int maxInCount = (len - off); |
||||||
|
int maxOutCount = (outBufLast - outPtr); |
||||||
|
|
||||||
|
if (maxInCount > maxOutCount) { |
||||||
|
maxInCount = maxOutCount; |
||||||
|
} |
||||||
|
maxInCount += off; |
||||||
|
ascii_loop: |
||||||
|
while (true) { |
||||||
|
if (off >= maxInCount) { // done with max. ascii seq
|
||||||
|
continue output_loop; |
||||||
|
} |
||||||
|
c = cbuf[off++]; |
||||||
|
if (c >= 0x80) { |
||||||
|
break ascii_loop; |
||||||
|
} |
||||||
|
outBuf[outPtr++] = (byte) c; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Nope, multi-byte:
|
||||||
|
if (c < 0x800) { // 2-byte
|
||||||
|
outBuf[outPtr++] = (byte) (0xc0 | (c >> 6)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} else { // 3 or 4 bytes
|
||||||
|
// Surrogates?
|
||||||
|
if (c < SURR1_FIRST || c > SURR2_LAST) { |
||||||
|
outBuf[outPtr++] = (byte) (0xe0 | (c >> 12)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
continue; |
||||||
|
} |
||||||
|
// Yup, a surrogate:
|
||||||
|
if (c > SURR1_LAST) { // must be from first range
|
||||||
|
_outPtr = outPtr; |
||||||
|
illegalSurrogate(c); |
||||||
|
} |
||||||
|
_surrogate = c; |
||||||
|
// and if so, followed by another from next range
|
||||||
|
if (off >= len) { // unless we hit the end?
|
||||||
|
break; |
||||||
|
} |
||||||
|
c = convertSurrogate(cbuf[off++]); |
||||||
|
if (c > 0x10FFFF) { // illegal in JSON as well as in XML
|
||||||
|
_outPtr = outPtr; |
||||||
|
illegalSurrogate(c); |
||||||
|
} |
||||||
|
outBuf[outPtr++] = (byte) (0xf0 | (c >> 18)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} |
||||||
|
} |
||||||
|
_outPtr = outPtr; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(int c) throws IOException |
||||||
|
{ |
||||||
|
// First; do we have a left over surrogate?
|
||||||
|
if (_surrogate > 0) { |
||||||
|
c = convertSurrogate(c); |
||||||
|
// If not, do we start with a surrogate?
|
||||||
|
} else if (c >= SURR1_FIRST && c <= SURR2_LAST) { |
||||||
|
// Illegal to get second part without first:
|
||||||
|
if (c > SURR1_LAST) { |
||||||
|
illegalSurrogate(c); |
||||||
|
} |
||||||
|
// First part just needs to be held for now
|
||||||
|
_surrogate = c; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (_outPtr >= _outBufferEnd) { // let's require enough room, first
|
||||||
|
_out.write(_outBuffer, 0, _outPtr); |
||||||
|
_outPtr = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (c < 0x80) { // ascii
|
||||||
|
_outBuffer[_outPtr++] = (byte) c; |
||||||
|
} else { |
||||||
|
int ptr = _outPtr; |
||||||
|
if (c < 0x800) { // 2-byte
|
||||||
|
_outBuffer[ptr++] = (byte) (0xc0 | (c >> 6)); |
||||||
|
_outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} else if (c <= 0xFFFF) { // 3 bytes
|
||||||
|
_outBuffer[ptr++] = (byte) (0xe0 | (c >> 12)); |
||||||
|
_outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
_outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} else { // 4 bytes
|
||||||
|
if (c > 0x10FFFF) { // illegal
|
||||||
|
illegalSurrogate(c); |
||||||
|
} |
||||||
|
_outBuffer[ptr++] = (byte) (0xf0 | (c >> 18)); |
||||||
|
_outBuffer[ptr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); |
||||||
|
_outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
_outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} |
||||||
|
_outPtr = ptr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String str) throws IOException |
||||||
|
{ |
||||||
|
write(str, 0, str.length()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(String str, int off, int len) throws IOException |
||||||
|
{ |
||||||
|
if (len < 2) { |
||||||
|
if (len == 1) { |
||||||
|
write(str.charAt(off)); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// First: do we have a leftover surrogate to deal with?
|
||||||
|
if (_surrogate > 0) { |
||||||
|
char second = str.charAt(off++); |
||||||
|
--len; |
||||||
|
write(convertSurrogate(second)); |
||||||
|
// will have at least one more char (case of 1 char was checked earlier on)
|
||||||
|
} |
||||||
|
|
||||||
|
int outPtr = _outPtr; |
||||||
|
byte[] outBuf = _outBuffer; |
||||||
|
int outBufLast = _outBufferEnd; // has 4 'spare' bytes
|
||||||
|
|
||||||
|
// All right; can just loop it nice and easy now:
|
||||||
|
len += off; // len will now be the end of input buffer
|
||||||
|
|
||||||
|
output_loop: |
||||||
|
for (; off < len; ) { |
||||||
|
/* First, let's ensure we can output at least 4 bytes |
||||||
|
* (longest UTF-8 encoded codepoint): |
||||||
|
*/ |
||||||
|
if (outPtr >= outBufLast) { |
||||||
|
_out.write(outBuf, 0, outPtr); |
||||||
|
outPtr = 0; |
||||||
|
} |
||||||
|
|
||||||
|
int c = str.charAt(off++); |
||||||
|
// And then see if we have an Ascii char:
|
||||||
|
if (c < 0x80) { // If so, can do a tight inner loop:
|
||||||
|
outBuf[outPtr++] = (byte)c; |
||||||
|
// Let's calc how many ascii chars we can copy at most:
|
||||||
|
int maxInCount = (len - off); |
||||||
|
int maxOutCount = (outBufLast - outPtr); |
||||||
|
|
||||||
|
if (maxInCount > maxOutCount) { |
||||||
|
maxInCount = maxOutCount; |
||||||
|
} |
||||||
|
maxInCount += off; |
||||||
|
ascii_loop: |
||||||
|
while (true) { |
||||||
|
if (off >= maxInCount) { // done with max. ascii seq
|
||||||
|
continue output_loop; |
||||||
|
} |
||||||
|
c = str.charAt(off++); |
||||||
|
if (c >= 0x80) { |
||||||
|
break ascii_loop; |
||||||
|
} |
||||||
|
outBuf[outPtr++] = (byte) c; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Nope, multi-byte:
|
||||||
|
if (c < 0x800) { // 2-byte
|
||||||
|
outBuf[outPtr++] = (byte) (0xc0 | (c >> 6)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} else { // 3 or 4 bytes
|
||||||
|
// Surrogates?
|
||||||
|
if (c < SURR1_FIRST || c > SURR2_LAST) { |
||||||
|
outBuf[outPtr++] = (byte) (0xe0 | (c >> 12)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
continue; |
||||||
|
} |
||||||
|
// Yup, a surrogate:
|
||||||
|
if (c > SURR1_LAST) { // must be from first range
|
||||||
|
_outPtr = outPtr; |
||||||
|
illegalSurrogate(c); |
||||||
|
} |
||||||
|
_surrogate = c; |
||||||
|
// and if so, followed by another from next range
|
||||||
|
if (off >= len) { // unless we hit the end?
|
||||||
|
break; |
||||||
|
} |
||||||
|
c = convertSurrogate(str.charAt(off++)); |
||||||
|
if (c > 0x10FFFF) { // illegal, as per RFC 4627
|
||||||
|
_outPtr = outPtr; |
||||||
|
illegalSurrogate(c); |
||||||
|
} |
||||||
|
outBuf[outPtr++] = (byte) (0xf0 | (c >> 18)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); |
||||||
|
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); |
||||||
|
} |
||||||
|
} |
||||||
|
_outPtr = outPtr; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called to calculate UTF codepoint, from a surrogate pair. |
||||||
|
*/ |
||||||
|
protected int convertSurrogate(int secondPart) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
int firstPart = _surrogate; |
||||||
|
_surrogate = 0; |
||||||
|
|
||||||
|
// Ok, then, is the second part valid?
|
||||||
|
if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) { |
||||||
|
throw new IOException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination"); |
||||||
|
} |
||||||
|
return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST); |
||||||
|
} |
||||||
|
|
||||||
|
protected static void illegalSurrogate(int code) throws IOException { |
||||||
|
throw new IOException(illegalSurrogateDesc(code)); |
||||||
|
} |
||||||
|
|
||||||
|
protected static String illegalSurrogateDesc(int code) |
||||||
|
{ |
||||||
|
if (code > 0x10FFFF) { // over max?
|
||||||
|
return "Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 4627"; |
||||||
|
} |
||||||
|
if (code >= SURR1_FIRST) { |
||||||
|
if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?)
|
||||||
|
return "Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")"; |
||||||
|
} |
||||||
|
return "Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")"; |
||||||
|
} |
||||||
|
// should we ever get this?
|
||||||
|
return "Illegal character point (0x"+Integer.toHexString(code)+") to output"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,513 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.json; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
import com.fr.third.fasterxml.jackson.core.format.InputAccessor; |
||||||
|
import com.fr.third.fasterxml.jackson.core.format.MatchStrength; |
||||||
|
import com.fr.third.fasterxml.jackson.core.io.*; |
||||||
|
import com.fr.third.fasterxml.jackson.core.sym.BytesToNameCanonicalizer; |
||||||
|
import com.fr.third.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is used to determine the encoding of byte stream |
||||||
|
* that is to contain JSON content. Rules are fairly simple, and |
||||||
|
* defined in JSON specification (RFC-4627 or newer), except |
||||||
|
* for BOM handling, which is a property of underlying |
||||||
|
* streams. |
||||||
|
*/ |
||||||
|
public final class ByteSourceJsonBootstrapper |
||||||
|
{ |
||||||
|
final static byte UTF8_BOM_1 = (byte) 0xEF; |
||||||
|
final static byte UTF8_BOM_2 = (byte) 0xBB; |
||||||
|
final static byte UTF8_BOM_3 = (byte) 0xBF; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected final IOContext _context; |
||||||
|
|
||||||
|
protected final InputStream _in; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Input buffering |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected final byte[] _inputBuffer; |
||||||
|
|
||||||
|
private int _inputPtr; |
||||||
|
|
||||||
|
private int _inputEnd; |
||||||
|
|
||||||
|
/** |
||||||
|
* Flag that indicates whether buffer above is to be recycled |
||||||
|
* after being used or not. |
||||||
|
*/ |
||||||
|
private final boolean _bufferRecyclable; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Input location |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Current number of input units (bytes or chars) that were processed in |
||||||
|
* previous blocks, |
||||||
|
* before contents of current input buffer. |
||||||
|
*<p> |
||||||
|
* Note: includes possible BOMs, if those were part of the input. |
||||||
|
*/ |
||||||
|
protected int _inputProcessed; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Data gathered |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected boolean _bigEndian = true; |
||||||
|
|
||||||
|
protected int _bytesPerChar = 0; // 0 means "dunno yet"
|
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public ByteSourceJsonBootstrapper(IOContext ctxt, InputStream in) |
||||||
|
{ |
||||||
|
_context = ctxt; |
||||||
|
_in = in; |
||||||
|
_inputBuffer = ctxt.allocReadIOBuffer(); |
||||||
|
_inputEnd = _inputPtr = 0; |
||||||
|
_inputProcessed = 0; |
||||||
|
_bufferRecyclable = true; |
||||||
|
} |
||||||
|
|
||||||
|
public ByteSourceJsonBootstrapper(IOContext ctxt, byte[] inputBuffer, int inputStart, int inputLen) |
||||||
|
{ |
||||||
|
_context = ctxt; |
||||||
|
_in = null; |
||||||
|
_inputBuffer = inputBuffer; |
||||||
|
_inputPtr = inputStart; |
||||||
|
_inputEnd = (inputStart + inputLen); |
||||||
|
// Need to offset this for correct location info
|
||||||
|
_inputProcessed = -inputStart; |
||||||
|
_bufferRecyclable = false; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Encoding detection during bootstrapping |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that should be called after constructing an instace. |
||||||
|
* It will figure out encoding that content uses, to allow |
||||||
|
* for instantiating a proper scanner object. |
||||||
|
*/ |
||||||
|
public JsonEncoding detectEncoding() |
||||||
|
throws IOException, JsonParseException |
||||||
|
{ |
||||||
|
boolean foundEncoding = false; |
||||||
|
|
||||||
|
// First things first: BOM handling
|
||||||
|
/* Note: we can require 4 bytes to be read, since no |
||||||
|
* combination of BOM + valid JSON content can have |
||||||
|
* shorter length (shortest valid JSON content is single |
||||||
|
* digit char, but BOMs are chosen such that combination |
||||||
|
* is always at least 4 chars long) |
||||||
|
*/ |
||||||
|
if (ensureLoaded(4)) { |
||||||
|
int quad = (_inputBuffer[_inputPtr] << 24) |
||||||
|
| ((_inputBuffer[_inputPtr+1] & 0xFF) << 16) |
||||||
|
| ((_inputBuffer[_inputPtr+2] & 0xFF) << 8) |
||||||
|
| (_inputBuffer[_inputPtr+3] & 0xFF); |
||||||
|
|
||||||
|
if (handleBOM(quad)) { |
||||||
|
foundEncoding = true; |
||||||
|
} else { |
||||||
|
/* If no BOM, need to auto-detect based on first char; |
||||||
|
* this works since it must be 7-bit ascii (wrt. unicode |
||||||
|
* compatible encodings, only ones JSON can be transferred |
||||||
|
* over) |
||||||
|
*/ |
||||||
|
// UTF-32?
|
||||||
|
if (checkUTF32(quad)) { |
||||||
|
foundEncoding = true; |
||||||
|
} else if (checkUTF16(quad >>> 16)) { |
||||||
|
foundEncoding = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (ensureLoaded(2)) { |
||||||
|
int i16 = ((_inputBuffer[_inputPtr] & 0xFF) << 8) |
||||||
|
| (_inputBuffer[_inputPtr+1] & 0xFF); |
||||||
|
if (checkUTF16(i16)) { |
||||||
|
foundEncoding = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
JsonEncoding enc; |
||||||
|
|
||||||
|
/* Not found yet? As per specs, this means it must be UTF-8. */ |
||||||
|
if (!foundEncoding) { |
||||||
|
enc = JsonEncoding.UTF8; |
||||||
|
} else { |
||||||
|
switch (_bytesPerChar) { |
||||||
|
case 1: |
||||||
|
enc = JsonEncoding.UTF8; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
enc = _bigEndian ? JsonEncoding.UTF16_BE : JsonEncoding.UTF16_LE; |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
enc = _bigEndian ? JsonEncoding.UTF32_BE : JsonEncoding.UTF32_LE; |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new RuntimeException("Internal error"); // should never get here
|
||||||
|
} |
||||||
|
} |
||||||
|
_context.setEncoding(enc); |
||||||
|
return enc; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Constructing a Reader |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@SuppressWarnings("resource") |
||||||
|
public Reader constructReader() |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
JsonEncoding enc = _context.getEncoding(); |
||||||
|
switch (enc.bits()) { |
||||||
|
case 8: // only in non-common case where we don't want to do direct mapping
|
||||||
|
case 16: |
||||||
|
{ |
||||||
|
// First: do we have a Stream? If not, need to create one:
|
||||||
|
InputStream in = _in; |
||||||
|
|
||||||
|
if (in == null) { |
||||||
|
in = new ByteArrayInputStream(_inputBuffer, _inputPtr, _inputEnd); |
||||||
|
} else { |
||||||
|
/* Also, if we have any read but unused input (usually true), |
||||||
|
* need to merge that input in: |
||||||
|
*/ |
||||||
|
if (_inputPtr < _inputEnd) { |
||||||
|
in = new MergedStream(_context, in, _inputBuffer, _inputPtr, _inputEnd); |
||||||
|
} |
||||||
|
} |
||||||
|
return new InputStreamReader(in, enc.getJavaName()); |
||||||
|
} |
||||||
|
case 32: |
||||||
|
return new UTF32Reader(_context, _in, _inputBuffer, _inputPtr, _inputEnd, |
||||||
|
_context.getEncoding().isBigEndian()); |
||||||
|
} |
||||||
|
throw new RuntimeException("Internal error"); // should never get here
|
||||||
|
} |
||||||
|
|
||||||
|
public JsonParser constructParser(int parserFeatures, ObjectCodec codec, |
||||||
|
BytesToNameCanonicalizer rootByteSymbols, CharsToNameCanonicalizer rootCharSymbols, |
||||||
|
boolean canonicalize, boolean intern) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
JsonEncoding enc = detectEncoding(); |
||||||
|
|
||||||
|
if (enc == JsonEncoding.UTF8) { |
||||||
|
/* and without canonicalization, byte-based approach is not performance; just use std UTF-8 reader |
||||||
|
* (which is ok for larger input; not so hot for smaller; but this is not a common case) |
||||||
|
*/ |
||||||
|
if (canonicalize) { |
||||||
|
BytesToNameCanonicalizer can = rootByteSymbols.makeChild(canonicalize, intern); |
||||||
|
return new UTF8StreamJsonParser(_context, parserFeatures, _in, codec, can, _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable); |
||||||
|
} |
||||||
|
} |
||||||
|
return new ReaderBasedJsonParser(_context, parserFeatures, constructReader(), codec, |
||||||
|
rootCharSymbols.makeChild(canonicalize, intern)); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Encoding detection for data format auto-detection |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Current implementation is not as thorough as other functionality |
||||||
|
* ({@link com.fr.third.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper}); |
||||||
|
* supports UTF-8, for example. But it should work, for now, and can |
||||||
|
* be improved as necessary. |
||||||
|
*/ |
||||||
|
public static MatchStrength hasJSONFormat(InputAccessor acc) throws IOException |
||||||
|
{ |
||||||
|
// Ideally we should see "[" or "{"; but if not, we'll accept double-quote (String)
|
||||||
|
// in future could also consider accepting non-standard matches?
|
||||||
|
|
||||||
|
if (!acc.hasMoreBytes()) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
byte b = acc.nextByte(); |
||||||
|
// Very first thing, a UTF-8 BOM?
|
||||||
|
if (b == UTF8_BOM_1) { // yes, looks like UTF-8 BOM
|
||||||
|
if (!acc.hasMoreBytes()) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
if (acc.nextByte() != UTF8_BOM_2) { |
||||||
|
return MatchStrength.NO_MATCH; |
||||||
|
} |
||||||
|
if (!acc.hasMoreBytes()) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
if (acc.nextByte() != UTF8_BOM_3) { |
||||||
|
return MatchStrength.NO_MATCH; |
||||||
|
} |
||||||
|
if (!acc.hasMoreBytes()) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
b = acc.nextByte(); |
||||||
|
} |
||||||
|
// Then possible leading space
|
||||||
|
int ch = skipSpace(acc, b); |
||||||
|
if (ch < 0) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
// First, let's see if it looks like a structured type:
|
||||||
|
if (ch == '{') { // JSON object?
|
||||||
|
// Ideally we need to find either double-quote or closing bracket
|
||||||
|
ch = skipSpace(acc); |
||||||
|
if (ch < 0) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
if (ch == '"' || ch == '}') { |
||||||
|
return MatchStrength.SOLID_MATCH; |
||||||
|
} |
||||||
|
// ... should we allow non-standard? Let's not yet... can add if need be
|
||||||
|
return MatchStrength.NO_MATCH; |
||||||
|
} |
||||||
|
MatchStrength strength; |
||||||
|
|
||||||
|
if (ch == '[') { |
||||||
|
ch = skipSpace(acc); |
||||||
|
if (ch < 0) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
// closing brackets is easy; but for now, let's also accept opening...
|
||||||
|
if (ch == ']' || ch == '[') { |
||||||
|
return MatchStrength.SOLID_MATCH; |
||||||
|
} |
||||||
|
return MatchStrength.SOLID_MATCH; |
||||||
|
} else { |
||||||
|
// plain old value is not very convincing...
|
||||||
|
strength = MatchStrength.WEAK_MATCH; |
||||||
|
} |
||||||
|
|
||||||
|
if (ch == '"') { // string value
|
||||||
|
return strength; |
||||||
|
} |
||||||
|
if (ch <= '9' && ch >= '0') { // number
|
||||||
|
return strength; |
||||||
|
} |
||||||
|
if (ch == '-') { // negative number
|
||||||
|
ch = skipSpace(acc); |
||||||
|
if (ch < 0) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
return (ch <= '9' && ch >= '0') ? strength : MatchStrength.NO_MATCH; |
||||||
|
} |
||||||
|
// or one of literals
|
||||||
|
if (ch == 'n') { // null
|
||||||
|
return tryMatch(acc, "ull", strength); |
||||||
|
} |
||||||
|
if (ch == 't') { // true
|
||||||
|
return tryMatch(acc, "rue", strength); |
||||||
|
} |
||||||
|
if (ch == 'f') { // false
|
||||||
|
return tryMatch(acc, "alse", strength); |
||||||
|
} |
||||||
|
return MatchStrength.NO_MATCH; |
||||||
|
} |
||||||
|
|
||||||
|
private static MatchStrength tryMatch(InputAccessor acc, String matchStr, MatchStrength fullMatchStrength) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
for (int i = 0, len = matchStr.length(); i < len; ++i) { |
||||||
|
if (!acc.hasMoreBytes()) { |
||||||
|
return MatchStrength.INCONCLUSIVE; |
||||||
|
} |
||||||
|
if (acc.nextByte() != matchStr.charAt(i)) { |
||||||
|
return MatchStrength.NO_MATCH; |
||||||
|
} |
||||||
|
} |
||||||
|
return fullMatchStrength; |
||||||
|
} |
||||||
|
|
||||||
|
private static int skipSpace(InputAccessor acc) throws IOException |
||||||
|
{ |
||||||
|
if (!acc.hasMoreBytes()) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return skipSpace(acc, acc.nextByte()); |
||||||
|
} |
||||||
|
|
||||||
|
private static int skipSpace(InputAccessor acc, byte b) throws IOException |
||||||
|
{ |
||||||
|
while (true) { |
||||||
|
int ch = (int) b & 0xFF; |
||||||
|
if (!(ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t')) { |
||||||
|
return ch; |
||||||
|
} |
||||||
|
if (!acc.hasMoreBytes()) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
b = acc.nextByte(); |
||||||
|
ch = (int) b & 0xFF; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods, parsing |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @return True if a BOM was succesfully found, and encoding |
||||||
|
* thereby recognized. |
||||||
|
*/ |
||||||
|
private boolean handleBOM(int quad) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
/* Handling of (usually) optional BOM (required for |
||||||
|
* multi-byte formats); first 32-bit charsets: |
||||||
|
*/ |
||||||
|
switch (quad) { |
||||||
|
case 0x0000FEFF: |
||||||
|
_bigEndian = true; |
||||||
|
_inputPtr += 4; |
||||||
|
_bytesPerChar = 4; |
||||||
|
return true; |
||||||
|
case 0xFFFE0000: // UCS-4, LE?
|
||||||
|
_inputPtr += 4; |
||||||
|
_bytesPerChar = 4; |
||||||
|
_bigEndian = false; |
||||||
|
return true; |
||||||
|
case 0x0000FFFE: // UCS-4, in-order...
|
||||||
|
reportWeirdUCS4("2143"); // throws exception
|
||||||
|
case 0xFEFF0000: // UCS-4, in-order...
|
||||||
|
reportWeirdUCS4("3412"); // throws exception
|
||||||
|
} |
||||||
|
// Ok, if not, how about 16-bit encoding BOMs?
|
||||||
|
int msw = quad >>> 16; |
||||||
|
if (msw == 0xFEFF) { // UTF-16, BE
|
||||||
|
_inputPtr += 2; |
||||||
|
_bytesPerChar = 2; |
||||||
|
_bigEndian = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (msw == 0xFFFE) { // UTF-16, LE
|
||||||
|
_inputPtr += 2; |
||||||
|
_bytesPerChar = 2; |
||||||
|
_bigEndian = false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
// And if not, then UTF-8 BOM?
|
||||||
|
if ((quad >>> 8) == 0xEFBBBF) { // UTF-8
|
||||||
|
_inputPtr += 3; |
||||||
|
_bytesPerChar = 1; |
||||||
|
_bigEndian = true; // doesn't really matter
|
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean checkUTF32(int quad) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
/* Handling of (usually) optional BOM (required for |
||||||
|
* multi-byte formats); first 32-bit charsets: |
||||||
|
*/ |
||||||
|
if ((quad >> 8) == 0) { // 0x000000?? -> UTF32-BE
|
||||||
|
_bigEndian = true; |
||||||
|
} else if ((quad & 0x00FFFFFF) == 0) { // 0x??000000 -> UTF32-LE
|
||||||
|
_bigEndian = false; |
||||||
|
} else if ((quad & ~0x00FF0000) == 0) { // 0x00??0000 -> UTF32-in-order
|
||||||
|
reportWeirdUCS4("3412"); |
||||||
|
} else if ((quad & ~0x0000FF00) == 0) { // 0x0000??00 -> UTF32-in-order
|
||||||
|
reportWeirdUCS4("2143"); |
||||||
|
} else { |
||||||
|
// Can not be valid UTF-32 encoded JSON...
|
||||||
|
return false; |
||||||
|
} |
||||||
|
// Not BOM (just regular content), nothing to skip past:
|
||||||
|
//_inputPtr += 4;
|
||||||
|
_bytesPerChar = 4; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean checkUTF16(int i16) |
||||||
|
{ |
||||||
|
if ((i16 & 0xFF00) == 0) { // UTF-16BE
|
||||||
|
_bigEndian = true; |
||||||
|
} else if ((i16 & 0x00FF) == 0) { // UTF-16LE
|
||||||
|
_bigEndian = false; |
||||||
|
} else { // nope, not UTF-16
|
||||||
|
return false; |
||||||
|
} |
||||||
|
// Not BOM (just regular content), nothing to skip past:
|
||||||
|
//_inputPtr += 2;
|
||||||
|
_bytesPerChar = 2; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods, problem reporting |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
private void reportWeirdUCS4(String type) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
throw new CharConversionException("Unsupported UCS-4 endianness ("+type+") detected"); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods, raw input access |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected boolean ensureLoaded(int minimum) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
/* Let's assume here buffer has enough room -- this will always |
||||||
|
* be true for the limited used this method gets |
||||||
|
*/ |
||||||
|
int gotten = (_inputEnd - _inputPtr); |
||||||
|
while (gotten < minimum) { |
||||||
|
int count; |
||||||
|
|
||||||
|
if (_in == null) { // block source
|
||||||
|
count = -1; |
||||||
|
} else { |
||||||
|
count = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd); |
||||||
|
} |
||||||
|
if (count < 1) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
_inputEnd += count; |
||||||
|
gotten += count; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.json; |
||||||
|
|
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper class used if |
||||||
|
* {@link com.fr.third.fasterxml.jackson.core.JsonParser.Feature#STRICT_DUPLICATE_DETECTION} |
||||||
|
* is enabled. |
||||||
|
* Optimized to try to limit memory usage and processing overhead for smallest |
||||||
|
* entries, but without adding trashing (immutable objects would achieve optimal |
||||||
|
* memory usage but lead to significant number of discarded temp objects for |
||||||
|
* scopes with large number of entries). Another consideration is trying to limit |
||||||
|
* actual number of compiled classes as it contributes significantly to overall |
||||||
|
* jar size (due to linkage etc). |
||||||
|
* |
||||||
|
* @since 2.3 |
||||||
|
*/ |
||||||
|
public class DupDetector |
||||||
|
{ |
||||||
|
/** |
||||||
|
* We need to store a back-reference here to parser/generator, unfortunately. |
||||||
|
*/ |
||||||
|
protected final Object _source; |
||||||
|
|
||||||
|
protected String _firstName; |
||||||
|
|
||||||
|
protected String _secondName; |
||||||
|
|
||||||
|
/** |
||||||
|
* Lazily constructed set of names already seen within this context. |
||||||
|
*/ |
||||||
|
protected HashSet<String> _seen; |
||||||
|
|
||||||
|
private DupDetector(Object src) { |
||||||
|
_source = src; |
||||||
|
} |
||||||
|
|
||||||
|
public static DupDetector rootDetector(JsonParser p) { |
||||||
|
return new DupDetector(p); |
||||||
|
} |
||||||
|
|
||||||
|
public static DupDetector rootDetector(JsonGenerator g) { |
||||||
|
return new DupDetector(g); |
||||||
|
} |
||||||
|
|
||||||
|
public DupDetector child() { |
||||||
|
return new DupDetector(_source); |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
_firstName = null; |
||||||
|
_secondName = null; |
||||||
|
_seen = null; |
||||||
|
} |
||||||
|
|
||||||
|
public JsonLocation findLocation() { |
||||||
|
// ugly but:
|
||||||
|
if (_source instanceof JsonParser) { |
||||||
|
return ((JsonParser)_source).getCurrentLocation(); |
||||||
|
} |
||||||
|
// do generators have a way to provide Location? Apparently not...
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isDup(String name) throws JsonParseException |
||||||
|
{ |
||||||
|
if (_firstName == null) { |
||||||
|
_firstName = name; |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (name.equals(_firstName)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (_secondName == null) { |
||||||
|
_secondName = name; |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (name.equals(_secondName)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (_seen == null) { |
||||||
|
_seen = new HashSet<String>(16); // 16 is default, seems reasonable
|
||||||
|
_seen.add(_firstName); |
||||||
|
_seen.add(_secondName); |
||||||
|
} |
||||||
|
return !_seen.add(name); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,172 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.json; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
import com.fr.third.fasterxml.jackson.core.base.GeneratorBase; |
||||||
|
import com.fr.third.fasterxml.jackson.core.io.CharTypes; |
||||||
|
import com.fr.third.fasterxml.jackson.core.io.CharacterEscapes; |
||||||
|
import com.fr.third.fasterxml.jackson.core.io.IOContext; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.DefaultPrettyPrinter; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.VersionUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* Intermediate base class shared by JSON-backed generators |
||||||
|
* like {@link UTF8JsonGenerator} and {@link WriterBasedJsonGenerator}. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public abstract class JsonGeneratorImpl extends GeneratorBase |
||||||
|
{ |
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Constants |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the default set of escape codes, over 7-bit ASCII range |
||||||
|
* (first 128 character codes), used for single-byte UTF-8 characters. |
||||||
|
*/ |
||||||
|
protected final static int[] sOutputEscapes = CharTypes.get7BitOutputEscapes(); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration, basic I/O |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
final protected IOContext _ioContext; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration, output escaping |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Currently active set of output escape code definitions (whether |
||||||
|
* and how to escape or not) for 7-bit ASCII range (first 128 |
||||||
|
* character codes). Defined separately to make potentially |
||||||
|
* customizable |
||||||
|
*/ |
||||||
|
protected int[] _outputEscapes = sOutputEscapes; |
||||||
|
|
||||||
|
/** |
||||||
|
* Value between 128 (0x80) and 65535 (0xFFFF) that indicates highest |
||||||
|
* Unicode code point that will not need escaping; or 0 to indicate |
||||||
|
* that all characters can be represented without escaping. |
||||||
|
* Typically used to force escaping of some portion of character set; |
||||||
|
* for example to always escape non-ASCII characters (if value was 127). |
||||||
|
*<p> |
||||||
|
* NOTE: not all sub-classes make use of this setting. |
||||||
|
*/ |
||||||
|
protected int _maximumNonEscapedChar; |
||||||
|
|
||||||
|
/** |
||||||
|
* Definition of custom character escapes to use for generators created |
||||||
|
* by this factory, if any. If null, standard data format specific |
||||||
|
* escapes are used. |
||||||
|
*/ |
||||||
|
protected CharacterEscapes _characterEscapes; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration, other |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Separator to use, if any, between root-level values. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
protected SerializableString _rootValueSeparator |
||||||
|
= DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec) |
||||||
|
{ |
||||||
|
super(features, codec); |
||||||
|
_ioContext = ctxt; |
||||||
|
if (isEnabled(Feature.ESCAPE_NON_ASCII)) { |
||||||
|
setHighestNonEscapedChar(127); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Overridden configuration methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator setHighestNonEscapedChar(int charCode) { |
||||||
|
_maximumNonEscapedChar = (charCode < 0) ? 0 : charCode; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getHighestEscapedChar() { |
||||||
|
return _maximumNonEscapedChar; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator setCharacterEscapes(CharacterEscapes esc) |
||||||
|
{ |
||||||
|
_characterEscapes = esc; |
||||||
|
if (esc == null) { // revert to standard escapes
|
||||||
|
_outputEscapes = sOutputEscapes; |
||||||
|
} else { |
||||||
|
_outputEscapes = esc.getEscapeCodesForAscii(); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s |
||||||
|
* it creates. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public CharacterEscapes getCharacterEscapes() { |
||||||
|
return _characterEscapes; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonGenerator setRootValueSeparator(SerializableString sep) { |
||||||
|
_rootValueSeparator = sep; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Versioned |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public Version version() { |
||||||
|
return VersionUtil.versionFor(getClass()); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Partial API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
// // Overrides just to make things final, to possibly help with inlining
|
||||||
|
|
||||||
|
@Override |
||||||
|
public final void writeStringField(String fieldName, String value) |
||||||
|
throws IOException, JsonGenerationException |
||||||
|
{ |
||||||
|
writeFieldName(fieldName); |
||||||
|
writeString(value); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,227 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.json; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
import com.fr.third.fasterxml.jackson.core.io.CharTypes; |
||||||
|
|
||||||
|
/** |
||||||
|
* Extension of {@link JsonStreamContext}, which implements |
||||||
|
* core methods needed, and also exposes |
||||||
|
* more complete API to parser implementation classes. |
||||||
|
*/ |
||||||
|
public final class JsonReadContext |
||||||
|
extends JsonStreamContext |
||||||
|
{ |
||||||
|
// // // Configuration
|
||||||
|
|
||||||
|
/** |
||||||
|
* Parent context for this context; null for root context. |
||||||
|
*/ |
||||||
|
protected final JsonReadContext _parent; |
||||||
|
|
||||||
|
// // // Optional duplicate detection
|
||||||
|
|
||||||
|
protected final DupDetector _dups; |
||||||
|
|
||||||
|
// // // Location information (minus source reference)
|
||||||
|
|
||||||
|
protected int _lineNr; |
||||||
|
protected int _columnNr; |
||||||
|
|
||||||
|
protected String _currentName; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Simple instance reuse slots; speeds up things |
||||||
|
/* a bit (10-15%) for docs with lots of small |
||||||
|
/* arrays/objects (for which allocation was |
||||||
|
/* visible in profile stack frames) |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected JsonReadContext _child = null; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Instance construction, reuse |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public JsonReadContext(JsonReadContext parent, DupDetector dups, |
||||||
|
int type, int lineNr, int colNr) |
||||||
|
{ |
||||||
|
super(); |
||||||
|
_parent = parent; |
||||||
|
_dups = dups; |
||||||
|
_type = type; |
||||||
|
_lineNr = lineNr; |
||||||
|
_columnNr = colNr; |
||||||
|
_index = -1; |
||||||
|
} |
||||||
|
|
||||||
|
protected void reset(int type, int lineNr, int colNr) |
||||||
|
{ |
||||||
|
_type = type; |
||||||
|
_index = -1; |
||||||
|
_lineNr = lineNr; |
||||||
|
_columnNr = colNr; |
||||||
|
_currentName = null; |
||||||
|
if (_dups != null) { |
||||||
|
_dups.reset(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
public void trackDups(JsonParser jp) { |
||||||
|
_dups = DupDetector.rootDetector(jp); |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
// // // Factory methods
|
||||||
|
|
||||||
|
@Deprecated // since 2.3, use variant that takes dup detector
|
||||||
|
public static JsonReadContext createRootContext(int lineNr, int colNr) { |
||||||
|
return createRootContext(lineNr, colNr, null); |
||||||
|
} |
||||||
|
|
||||||
|
public static JsonReadContext createRootContext(int lineNr, int colNr, |
||||||
|
DupDetector dups) |
||||||
|
{ |
||||||
|
return new JsonReadContext(null, dups, TYPE_ROOT, lineNr, colNr); |
||||||
|
} |
||||||
|
|
||||||
|
@Deprecated // since 2.3, use variant that takes dup detector
|
||||||
|
public static JsonReadContext createRootContext() { |
||||||
|
return createRootContext(null); |
||||||
|
} |
||||||
|
|
||||||
|
public static JsonReadContext createRootContext(DupDetector dups) { |
||||||
|
return new JsonReadContext(null, dups, TYPE_ROOT, 1, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonReadContext createChildArrayContext(int lineNr, int colNr) |
||||||
|
{ |
||||||
|
JsonReadContext ctxt = _child; |
||||||
|
if (ctxt == null) { |
||||||
|
_child = ctxt = new JsonReadContext(this, |
||||||
|
(_dups == null) ? null : _dups.child(), |
||||||
|
TYPE_ARRAY, lineNr, colNr); |
||||||
|
} else { |
||||||
|
ctxt.reset(TYPE_ARRAY, lineNr, colNr); |
||||||
|
} |
||||||
|
return ctxt; |
||||||
|
} |
||||||
|
|
||||||
|
public JsonReadContext createChildObjectContext(int lineNr, int colNr) |
||||||
|
{ |
||||||
|
JsonReadContext ctxt = _child; |
||||||
|
if (ctxt == null) { |
||||||
|
_child = ctxt = new JsonReadContext(this, |
||||||
|
(_dups == null) ? null : _dups.child(), |
||||||
|
TYPE_OBJECT, lineNr, colNr); |
||||||
|
return ctxt; |
||||||
|
} |
||||||
|
ctxt.reset(TYPE_OBJECT, lineNr, colNr); |
||||||
|
return ctxt; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Abstract method implementation |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCurrentName() { return _currentName; } |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonReadContext getParent() { return _parent; } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Extended API |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Location pointing to the point where the context |
||||||
|
* start marker was found |
||||||
|
*/ |
||||||
|
public JsonLocation getStartLocation(Object srcRef) |
||||||
|
{ |
||||||
|
/* We don't keep track of offsets at this level (only |
||||||
|
* reader does) |
||||||
|
*/ |
||||||
|
long totalChars = -1L; |
||||||
|
|
||||||
|
return new JsonLocation(srcRef, totalChars, _lineNr, _columnNr); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* State changes |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public boolean expectComma() |
||||||
|
{ |
||||||
|
/* Assumption here is that we will be getting a value (at least |
||||||
|
* before calling this method again), and |
||||||
|
* so will auto-increment index to avoid having to do another call |
||||||
|
*/ |
||||||
|
int ix = ++_index; // starts from -1
|
||||||
|
return (_type != TYPE_ROOT && ix > 0); |
||||||
|
} |
||||||
|
|
||||||
|
public void setCurrentName(String name) throws JsonProcessingException |
||||||
|
{ |
||||||
|
_currentName = name; |
||||||
|
if (_dups != null) { |
||||||
|
_checkDup(_dups, name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void _checkDup(DupDetector dd, String name) throws JsonProcessingException |
||||||
|
{ |
||||||
|
if (dd.isDup(name)) { |
||||||
|
throw new JsonParseException("Duplicate field '"+name+"'", dd.findLocation()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Overridden standard methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to provide developer readable "JsonPath" representation |
||||||
|
* of the context. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String toString() |
||||||
|
{ |
||||||
|
StringBuilder sb = new StringBuilder(64); |
||||||
|
switch (_type) { |
||||||
|
case TYPE_ROOT: |
||||||
|
sb.append("/"); |
||||||
|
break; |
||||||
|
case TYPE_ARRAY: |
||||||
|
sb.append('['); |
||||||
|
sb.append(getCurrentIndex()); |
||||||
|
sb.append(']'); |
||||||
|
break; |
||||||
|
case TYPE_OBJECT: |
||||||
|
sb.append('{'); |
||||||
|
if (_currentName != null) { |
||||||
|
sb.append('"'); |
||||||
|
CharTypes.appendQuoted(sb, _currentName); |
||||||
|
sb.append('"'); |
||||||
|
} else { |
||||||
|
sb.append('?'); |
||||||
|
} |
||||||
|
sb.append('}'); |
||||||
|
break; |
||||||
|
} |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,208 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.json; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Extension of {@link JsonStreamContext}, which implements |
||||||
|
* core methods needed, and also exposes |
||||||
|
* more complete API to generator implementation classes. |
||||||
|
*/ |
||||||
|
public class JsonWriteContext |
||||||
|
extends JsonStreamContext |
||||||
|
{ |
||||||
|
// // // Return values for writeValue()
|
||||||
|
|
||||||
|
public final static int STATUS_OK_AS_IS = 0; |
||||||
|
public final static int STATUS_OK_AFTER_COMMA = 1; |
||||||
|
public final static int STATUS_OK_AFTER_COLON = 2; |
||||||
|
public final static int STATUS_OK_AFTER_SPACE = 3; // in root context
|
||||||
|
public final static int STATUS_EXPECT_VALUE = 4; |
||||||
|
public final static int STATUS_EXPECT_NAME = 5; |
||||||
|
|
||||||
|
/** |
||||||
|
* Parent context for this context; null for root context. |
||||||
|
*/ |
||||||
|
protected final JsonWriteContext _parent; |
||||||
|
|
||||||
|
// // // Optional duplicate detection
|
||||||
|
|
||||||
|
protected final DupDetector _dups; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Simple instance reuse slots; speed up things |
||||||
|
/* a bit (10-15%) for docs with lots of small |
||||||
|
/* arrays/objects |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected JsonWriteContext _child = null; |
||||||
|
|
||||||
|
/** |
||||||
|
* Name of the field of which value is to be parsed; only |
||||||
|
* used for OBJECT contexts |
||||||
|
*/ |
||||||
|
protected String _currentName; |
||||||
|
|
||||||
|
/** |
||||||
|
* Marker used to indicate that we just received a name, and |
||||||
|
* now expect a value |
||||||
|
*/ |
||||||
|
protected boolean _gotName; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
protected JsonWriteContext(int type, JsonWriteContext parent, |
||||||
|
DupDetector dups) |
||||||
|
{ |
||||||
|
super(); |
||||||
|
_type = type; |
||||||
|
_parent = parent; |
||||||
|
_dups = dups; |
||||||
|
_index = -1; |
||||||
|
} |
||||||
|
|
||||||
|
protected JsonWriteContext reset(int type) { |
||||||
|
_type = type; |
||||||
|
_index = -1; |
||||||
|
_currentName = null; |
||||||
|
_gotName = false; |
||||||
|
if (_dups != null) { |
||||||
|
_dups.reset(); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
// // // Factory methods
|
||||||
|
|
||||||
|
/** |
||||||
|
* @deprecated Since 2.3; use method that takes argument |
||||||
|
*/ |
||||||
|
@Deprecated |
||||||
|
public static JsonWriteContext createRootContext() { |
||||||
|
return createRootContext(null); |
||||||
|
} |
||||||
|
|
||||||
|
public static JsonWriteContext createRootContext(DupDetector dd) { |
||||||
|
return new JsonWriteContext(TYPE_ROOT, null, dd); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonWriteContext createChildArrayContext() |
||||||
|
{ |
||||||
|
JsonWriteContext ctxt = _child; |
||||||
|
if (ctxt == null) { |
||||||
|
_child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, |
||||||
|
(_dups == null) ? null : _dups.child()); |
||||||
|
return ctxt; |
||||||
|
} |
||||||
|
return ctxt.reset(TYPE_ARRAY); |
||||||
|
} |
||||||
|
|
||||||
|
public JsonWriteContext createChildObjectContext() |
||||||
|
{ |
||||||
|
JsonWriteContext ctxt = _child; |
||||||
|
if (ctxt == null) { |
||||||
|
_child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, |
||||||
|
(_dups == null) ? null : _dups.child()); |
||||||
|
return ctxt; |
||||||
|
} |
||||||
|
return ctxt.reset(TYPE_OBJECT); |
||||||
|
} |
||||||
|
|
||||||
|
// // // Shared API
|
||||||
|
|
||||||
|
@Override |
||||||
|
public final JsonWriteContext getParent() { return _parent; } |
||||||
|
|
||||||
|
@Override |
||||||
|
public final String getCurrentName() { return _currentName; } |
||||||
|
|
||||||
|
// // // API sub-classes are to implement
|
||||||
|
|
||||||
|
/** |
||||||
|
* Method that writer is to call before it writes a field name. |
||||||
|
* |
||||||
|
* @return Index of the field entry (0-based) |
||||||
|
*/ |
||||||
|
public final int writeFieldName(String name) throws JsonProcessingException |
||||||
|
{ |
||||||
|
_gotName = true; |
||||||
|
_currentName = name; |
||||||
|
if (_dups != null) { |
||||||
|
_checkDup(_dups, name); |
||||||
|
} |
||||||
|
return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA; |
||||||
|
} |
||||||
|
|
||||||
|
private void _checkDup(DupDetector dd, String name) throws JsonProcessingException |
||||||
|
{ |
||||||
|
if (dd.isDup(name)) { |
||||||
|
throw new JsonGenerationException("Duplicate field '"+name+"'"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public final int writeValue() |
||||||
|
{ |
||||||
|
// Most likely, object:
|
||||||
|
if (_type == TYPE_OBJECT) { |
||||||
|
_gotName = false; |
||||||
|
++_index; |
||||||
|
return STATUS_OK_AFTER_COLON; |
||||||
|
} |
||||||
|
|
||||||
|
// Ok, array?
|
||||||
|
if (_type == TYPE_ARRAY) { |
||||||
|
int ix = _index; |
||||||
|
++_index; |
||||||
|
return (ix < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA; |
||||||
|
} |
||||||
|
|
||||||
|
// Nope, root context
|
||||||
|
// No commas within root context, but need space
|
||||||
|
++_index; |
||||||
|
return (_index == 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_SPACE; |
||||||
|
} |
||||||
|
|
||||||
|
// // // Internally used abstract methods
|
||||||
|
|
||||||
|
protected final void appendDesc(StringBuilder sb) |
||||||
|
{ |
||||||
|
if (_type == TYPE_OBJECT) { |
||||||
|
sb.append('{'); |
||||||
|
if (_currentName != null) { |
||||||
|
sb.append('"'); |
||||||
|
// !!! TODO: Name chars should be escaped?
|
||||||
|
sb.append(_currentName); |
||||||
|
sb.append('"'); |
||||||
|
} else { |
||||||
|
sb.append('?'); |
||||||
|
} |
||||||
|
sb.append('}'); |
||||||
|
} else if (_type == TYPE_ARRAY) { |
||||||
|
sb.append('['); |
||||||
|
sb.append(getCurrentIndex()); |
||||||
|
sb.append(']'); |
||||||
|
} else { |
||||||
|
// nah, ROOT:
|
||||||
|
sb.append("/"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// // // Overridden standard methods
|
||||||
|
|
||||||
|
/** |
||||||
|
* Overridden to provide developer writeable "JsonPath" representation |
||||||
|
* of the context. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public final String toString() |
||||||
|
{ |
||||||
|
StringBuilder sb = new StringBuilder(64); |
||||||
|
appendDesc(sb); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package @package@; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.Version; |
||||||
|
import com.fr.third.fasterxml.jackson.core.Versioned; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.VersionUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* Automatically generated from PackageVersion.java.in during |
||||||
|
* packageVersion-generate execution of maven-replacer-plugin in |
||||||
|
* pom.xml. |
||||||
|
*/ |
||||||
|
public final class PackageVersion implements Versioned { |
||||||
|
public final static Version VERSION = VersionUtil.parseVersion( |
||||||
|
"@projectversion@", "@projectgroupid@", "@projectartifactid@"); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Version version() { |
||||||
|
return VERSION; |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@ |
|||||||
|
/** |
||||||
|
* JSON-specific parser and generator implementation classes that |
||||||
|
* Jackson defines and uses. |
||||||
|
* Application code should not (need to) use contents of this package; |
||||||
|
* nor are these implementations likely to be of use for sub-classing. |
||||||
|
*/ |
||||||
|
package com.fr.third.fasterxml.jackson.core.json; |
@ -0,0 +1,28 @@ |
|||||||
|
/** |
||||||
|
* Main public API classes of the core streaming JSON |
||||||
|
* processor: most importantly {@link com.fr.third.fasterxml.jackson.core.JsonFactory} |
||||||
|
* used for constructing |
||||||
|
* JSON parser ({@link com.fr.third.fasterxml.jackson.core.JsonParser}) |
||||||
|
* and generator |
||||||
|
* ({@link com.fr.third.fasterxml.jackson.core.JsonParser}) |
||||||
|
* instances. |
||||||
|
* <p> |
||||||
|
* Public API of the higher-level mapping interfaces ("Mapping API") |
||||||
|
* is found from the "jackson-databind" bundle, except for following |
||||||
|
* base interfaces that are defined here: |
||||||
|
* <ul> |
||||||
|
*<li>{@link com.fr.third.fasterxml.jackson.core.TreeNode} is included |
||||||
|
*within Streaming API to support integration of the Tree Model |
||||||
|
*(which is based on <code>JsonNode</code>) with the basic |
||||||
|
*parsers and generators (iff using mapping-supporting factory: which |
||||||
|
*is part of Mapping API, not core) |
||||||
|
* </li> |
||||||
|
*<li>{@link com.fr.third.fasterxml.jackson.core.ObjectCodec} is included so that |
||||||
|
* reference to the object capable of serializing/deserializing |
||||||
|
* Objects to/from JSON (usually, <code>com.fr.third.fasterxml.jackson.databind.ObjectMapper</code>) |
||||||
|
* can be exposed, without adding direct dependency to implementation. |
||||||
|
* </li> |
||||||
|
*</ul> |
||||||
|
*/ |
||||||
|
|
||||||
|
package com.fr.third.fasterxml.jackson.core; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,728 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.sym; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
import com.fr.third.fasterxml.jackson.core.util.ArraysCompat; |
||||||
|
import com.fr.third.fasterxml.jackson.core.util.InternCache; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is a kind of specialized type-safe Map, from char array to |
||||||
|
* String value. Specialization means that in addition to type-safety |
||||||
|
* and specific access patterns (key char array, Value optionally interned |
||||||
|
* String; values added on access if necessary), and that instances are |
||||||
|
* meant to be used concurrently, but by using well-defined mechanisms |
||||||
|
* to obtain such concurrently usable instances. Main use for the class
|
||||||
|
* is to store symbol table information for things like compilers and |
||||||
|
* parsers; especially when number of symbols (keywords) is limited. |
||||||
|
*<p> |
||||||
|
* For optimal performance, usage pattern should be one where matches |
||||||
|
* should be very common (especially after "warm-up"), and as with most hash-based |
||||||
|
* maps/sets, that hash codes are uniformly distributed. Also, collisions |
||||||
|
* are slightly more expensive than with HashMap or HashSet, since hash codes |
||||||
|
* are not used in resolving collisions; that is, equals() comparison is |
||||||
|
* done with all symbols in same bucket index.<br /> |
||||||
|
* Finally, rehashing is also more expensive, as hash codes are not |
||||||
|
* stored; rehashing requires all entries' hash codes to be recalculated. |
||||||
|
* Reason for not storing hash codes is reduced memory usage, hoping |
||||||
|
* for better memory locality. |
||||||
|
*<p> |
||||||
|
* Usual usage pattern is to create a single "master" instance, and either |
||||||
|
* use that instance in sequential fashion, or to create derived "child" |
||||||
|
* instances, which after use, are asked to return possible symbol additions |
||||||
|
* to master instance. In either case benefit is that symbol table gets |
||||||
|
* initialized so that further uses are more efficient, as eventually all |
||||||
|
* symbols needed will already be in symbol table. At that point no more |
||||||
|
* Symbol String allocations are needed, nor changes to symbol table itself. |
||||||
|
*<p> |
||||||
|
* Note that while individual SymbolTable instances are NOT thread-safe |
||||||
|
* (much like generic collection classes), concurrently used "child" |
||||||
|
* instances can be freely used without synchronization. However, using |
||||||
|
* master table concurrently with child instances can only be done if |
||||||
|
* access to master instance is read-only (i.e. no modifications done). |
||||||
|
*/ |
||||||
|
public final class CharsToNameCanonicalizer |
||||||
|
{ |
||||||
|
/* If we use "multiply-add" based hash algorithm, this is the multiplier |
||||||
|
* we use. |
||||||
|
*/ |
||||||
|
public final static int HASH_MULT = 33; |
||||||
|
|
||||||
|
/** |
||||||
|
* Default initial table size. Shouldn't be miniscule (as there's |
||||||
|
* cost to both array realloc and rehashing), but let's keep |
||||||
|
* it reasonably small nonetheless. For systems that properly |
||||||
|
* reuse factories it doesn't matter either way; but when |
||||||
|
* recreating factories often, initial overhead may dominate. |
||||||
|
*/ |
||||||
|
protected static final int DEFAULT_TABLE_SIZE = 64; |
||||||
|
|
||||||
|
/** |
||||||
|
* Let's not expand symbol tables past some maximum size; |
||||||
|
* this should protected against OOMEs caused by large documents |
||||||
|
* with uniquer (~= random) names. |
||||||
|
*/ |
||||||
|
protected static final int MAX_TABLE_SIZE = 0x10000; // 64k entries == 256k mem
|
||||||
|
|
||||||
|
/** |
||||||
|
* Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k; |
||||||
|
* this corresponds to 64k main hash index. This should allow for enough distinct |
||||||
|
* names for almost any case. |
||||||
|
*/ |
||||||
|
final static int MAX_ENTRIES_FOR_REUSE = 12000; |
||||||
|
|
||||||
|
/** |
||||||
|
* Also: to thwart attacks based on hash collisions (which may or may not |
||||||
|
* be cheap to calculate), we will need to detect "too long" |
||||||
|
* collision chains. Let's start with static value of 255 entries |
||||||
|
* for the longest legal chain. |
||||||
|
*<p> |
||||||
|
* Note: longest chain we have been able to produce without malicious |
||||||
|
* intent has been 38 (with "com.fr.third.fasterxml.jackson.core.main.TestWithTonsaSymbols"); |
||||||
|
* our setting should be reasonable here. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
final static int MAX_COLL_CHAIN_LENGTH = 255; |
||||||
|
|
||||||
|
/** |
||||||
|
* And to support reduce likelihood of accidental collisons causing |
||||||
|
* exceptions, let's prevent reuse of tables with long collision |
||||||
|
* overflow lists as well. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
final static int MAX_COLL_CHAIN_FOR_REUSE = 63; |
||||||
|
|
||||||
|
final static CharsToNameCanonicalizer sBootstrapSymbolTable; |
||||||
|
static { |
||||||
|
sBootstrapSymbolTable = new CharsToNameCanonicalizer(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Configuration |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Sharing of learnt symbols is done by optional linking of symbol |
||||||
|
* table instances with their parents. When parent linkage is |
||||||
|
* defined, and child instance is released (call to <code>release</code>), |
||||||
|
* parent's shared tables may be updated from the child instance. |
||||||
|
*/ |
||||||
|
protected CharsToNameCanonicalizer _parent; |
||||||
|
|
||||||
|
/** |
||||||
|
* Seed value we use as the base to make hash codes non-static between |
||||||
|
* different runs, but still stable for lifetime of a single symbol table |
||||||
|
* instance. |
||||||
|
* This is done for security reasons, to avoid potential DoS attack via |
||||||
|
* hash collisions. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
final private int _hashSeed; |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether canonical symbol Strings are to be intern()ed before added |
||||||
|
* to the table or not |
||||||
|
*/ |
||||||
|
final protected boolean _intern; |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether any canonicalization should be attempted (whether using |
||||||
|
* intern or not) |
||||||
|
*/ |
||||||
|
final protected boolean _canonicalize; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Actual symbol table data |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Primary matching symbols; it's expected most match occur from |
||||||
|
* here. |
||||||
|
*/ |
||||||
|
protected String[] _symbols; |
||||||
|
|
||||||
|
/** |
||||||
|
* Overflow buckets; if primary doesn't match, lookup is done |
||||||
|
* from here. |
||||||
|
*<p> |
||||||
|
* Note: Number of buckets is half of number of symbol entries, on |
||||||
|
* assumption there's less need for buckets. |
||||||
|
*/ |
||||||
|
protected Bucket[] _buckets; |
||||||
|
|
||||||
|
/** |
||||||
|
* Current size (number of entries); needed to know if and when |
||||||
|
* rehash. |
||||||
|
*/ |
||||||
|
protected int _size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Limit that indicates maximum size this instance can hold before |
||||||
|
* it needs to be expanded and rehashed. Calculated using fill |
||||||
|
* factor passed in to constructor. |
||||||
|
*/ |
||||||
|
protected int _sizeThreshold; |
||||||
|
|
||||||
|
/** |
||||||
|
* Mask used to get index from hash values; equal to |
||||||
|
* <code>_buckets.length - 1</code>, when _buckets.length is |
||||||
|
* a power of two. |
||||||
|
*/ |
||||||
|
protected int _indexMask; |
||||||
|
|
||||||
|
/** |
||||||
|
* We need to keep track of the longest collision list; this is needed |
||||||
|
* both to indicate problems with attacks and to allow flushing for |
||||||
|
* other cases. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
protected int _longestCollisionList; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* State regarding shared arrays |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Flag that indicates if any changes have been made to the data; |
||||||
|
* used to both determine if bucket array needs to be copied when |
||||||
|
* (first) change is made, and potentially if updated bucket list |
||||||
|
* is to be resync'ed back to master instance. |
||||||
|
*/ |
||||||
|
protected boolean _dirty; |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Life-cycle |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called to create root canonicalizer for a {@link com.fr.third.fasterxml.jackson.core.JsonFactory} |
||||||
|
* instance. Root instance is never used directly; its main use is for |
||||||
|
* storing and sharing underlying symbol arrays as needed. |
||||||
|
*/ |
||||||
|
public static CharsToNameCanonicalizer createRoot() |
||||||
|
{ |
||||||
|
/* [Issue-21]: Need to use a variable seed, to thwart hash-collision |
||||||
|
* based attacks. |
||||||
|
*/ |
||||||
|
long now = System.currentTimeMillis(); |
||||||
|
// ensure it's not 0; and might as well require to be odd so:
|
||||||
|
int seed = (((int) now) + ((int) (now >>> 32))) | 1; |
||||||
|
return createRoot(seed); |
||||||
|
} |
||||||
|
|
||||||
|
protected static CharsToNameCanonicalizer createRoot(int hashSeed) { |
||||||
|
return sBootstrapSymbolTable.makeOrphan(hashSeed); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Main method for constructing a master symbol table instance. |
||||||
|
* |
||||||
|
* @param initialSize Minimum initial size for bucket array; internally |
||||||
|
* will always use a power of two equal to or bigger than this value. |
||||||
|
*/ |
||||||
|
private CharsToNameCanonicalizer() |
||||||
|
{ |
||||||
|
// these settings don't really matter for the bootstrap instance
|
||||||
|
_canonicalize = true; |
||||||
|
_intern = true; |
||||||
|
// And we'll also set flags so no copying of buckets is needed:
|
||||||
|
_dirty = true; |
||||||
|
_hashSeed = 0; |
||||||
|
_longestCollisionList = 0; |
||||||
|
initTables(DEFAULT_TABLE_SIZE); |
||||||
|
} |
||||||
|
|
||||||
|
private void initTables(int initialSize) |
||||||
|
{ |
||||||
|
_symbols = new String[initialSize]; |
||||||
|
_buckets = new Bucket[initialSize >> 1]; |
||||||
|
// Mask is easy to calc for powers of two.
|
||||||
|
_indexMask = initialSize - 1; |
||||||
|
_size = 0; |
||||||
|
_longestCollisionList = 0; |
||||||
|
// Hard-coded fill factor is 75%
|
||||||
|
_sizeThreshold = _thresholdSize(initialSize); |
||||||
|
} |
||||||
|
|
||||||
|
private static int _thresholdSize(int hashAreaSize) { |
||||||
|
return hashAreaSize - (hashAreaSize >> 2); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Internal constructor used when creating child instances. |
||||||
|
*/ |
||||||
|
private CharsToNameCanonicalizer(CharsToNameCanonicalizer parent, |
||||||
|
boolean canonicalize, boolean intern, |
||||||
|
String[] symbols, Bucket[] buckets, int size, |
||||||
|
int hashSeed, int longestColl) |
||||||
|
{ |
||||||
|
_parent = parent; |
||||||
|
_canonicalize = canonicalize; |
||||||
|
_intern = intern; |
||||||
|
|
||||||
|
_symbols = symbols; |
||||||
|
_buckets = buckets; |
||||||
|
_size = size; |
||||||
|
_hashSeed = hashSeed; |
||||||
|
// Hard-coded fill factor, 75%
|
||||||
|
int arrayLen = (symbols.length); |
||||||
|
_sizeThreshold = _thresholdSize(arrayLen); |
||||||
|
_indexMask = (arrayLen - 1); |
||||||
|
_longestCollisionList = longestColl; |
||||||
|
|
||||||
|
// Need to make copies of arrays, if/when adding new entries
|
||||||
|
_dirty = false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* "Factory" method; will create a new child instance of this symbol |
||||||
|
* table. It will be a copy-on-write instance, ie. it will only use |
||||||
|
* read-only copy of parent's data, but when changes are needed, a |
||||||
|
* copy will be created. |
||||||
|
*<p> |
||||||
|
* Note: while this method is synchronized, it is generally not |
||||||
|
* safe to both use makeChild/mergeChild, AND to use instance |
||||||
|
* actively. Instead, a separate 'root' instance should be used |
||||||
|
* on which only makeChild/mergeChild are called, but instance itself |
||||||
|
* is not used as a symbol table. |
||||||
|
*/ |
||||||
|
public CharsToNameCanonicalizer makeChild(final boolean canonicalize, |
||||||
|
final boolean intern) |
||||||
|
{ |
||||||
|
/* 24-Jul-2012, tatu: Trying to reduce scope of synchronization, assuming |
||||||
|
* that synchronizing construction is the (potentially) expensive part, |
||||||
|
* and not so much short copy-the-variables thing. |
||||||
|
*/ |
||||||
|
final String[] symbols; |
||||||
|
final Bucket[] buckets; |
||||||
|
final int size; |
||||||
|
final int hashSeed; |
||||||
|
final int longestCollisionList; |
||||||
|
|
||||||
|
synchronized (this) { |
||||||
|
symbols = _symbols; |
||||||
|
buckets = _buckets; |
||||||
|
size = _size; |
||||||
|
hashSeed = _hashSeed; |
||||||
|
longestCollisionList = _longestCollisionList; |
||||||
|
} |
||||||
|
|
||||||
|
return new CharsToNameCanonicalizer(this, canonicalize, intern, |
||||||
|
symbols, buckets, size, hashSeed, longestCollisionList); |
||||||
|
} |
||||||
|
|
||||||
|
private CharsToNameCanonicalizer makeOrphan(int seed) |
||||||
|
{ |
||||||
|
return new CharsToNameCanonicalizer(null, true, true, |
||||||
|
_symbols, _buckets, _size, seed, _longestCollisionList); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method that allows contents of child table to potentially be |
||||||
|
* "merged in" with contents of this symbol table. |
||||||
|
*<p> |
||||||
|
* Note that caller has to make sure symbol table passed in is |
||||||
|
* really a child or sibling of this symbol table. |
||||||
|
*/ |
||||||
|
private void mergeChild(CharsToNameCanonicalizer child) |
||||||
|
{ |
||||||
|
/* One caveat: let's try to avoid problems with |
||||||
|
* degenerate cases of documents with generated "random" |
||||||
|
* names: for these, symbol tables would bloat indefinitely. |
||||||
|
* One way to do this is to just purge tables if they grow |
||||||
|
* too large, and that's what we'll do here. |
||||||
|
*/ |
||||||
|
if (child.size() > MAX_ENTRIES_FOR_REUSE |
||||||
|
|| child._longestCollisionList > MAX_COLL_CHAIN_FOR_REUSE) { |
||||||
|
// Should there be a way to get notified about this event, to log it or such?
|
||||||
|
// (as it's somewhat abnormal thing to happen)
|
||||||
|
// At any rate, need to clean up the tables, then:
|
||||||
|
synchronized (this) { |
||||||
|
initTables(DEFAULT_TABLE_SIZE); |
||||||
|
// Dirty flag... well, let's just clear it. Shouldn't really matter for master tables
|
||||||
|
// (which this is, given something is merged to it)
|
||||||
|
_dirty = false; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Otherwise, we'll merge changed stuff in, if there are more entries (which
|
||||||
|
// may not be the case if one of siblings has added symbols first or such)
|
||||||
|
if (child.size() <= size()) { // nothing to add
|
||||||
|
return; |
||||||
|
} |
||||||
|
// Okie dokie, let's get the data in!
|
||||||
|
synchronized (this) { |
||||||
|
_symbols = child._symbols; |
||||||
|
_buckets = child._buckets; |
||||||
|
_size = child._size; |
||||||
|
_sizeThreshold = child._sizeThreshold; |
||||||
|
_indexMask = child._indexMask; |
||||||
|
_longestCollisionList = child._longestCollisionList; |
||||||
|
// Dirty flag... well, let's just clear it. Shouldn't really matter for master tables
|
||||||
|
// (which this is, given something is merged to it)
|
||||||
|
_dirty = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void release() |
||||||
|
{ |
||||||
|
// If nothing has been added, nothing to do
|
||||||
|
if (!maybeDirty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (_parent != null) { |
||||||
|
_parent.mergeChild(this); |
||||||
|
/* Let's also mark this instance as dirty, so that just in |
||||||
|
* case release was too early, there's no corruption |
||||||
|
* of possibly shared data. |
||||||
|
*/ |
||||||
|
_dirty = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, generic accessors: |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public int size() { return _size; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Method for checking number of primary hash buckets this symbol |
||||||
|
* table uses. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public int bucketCount() { |
||||||
|
return _symbols.length; } |
||||||
|
|
||||||
|
public boolean maybeDirty() { return _dirty; } |
||||||
|
|
||||||
|
public int hashSeed() { return _hashSeed; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Method mostly needed by unit tests; calculates number of |
||||||
|
* entries that are in collision list. Value can be at most |
||||||
|
* ({@link #size} - 1), but should usually be much lower, ideally 0. |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public int collisionCount() |
||||||
|
{ |
||||||
|
int count = 0; |
||||||
|
|
||||||
|
for (Bucket bucket : _buckets) { |
||||||
|
if (bucket != null) { |
||||||
|
count += bucket.length(); |
||||||
|
} |
||||||
|
} |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method mostly needed by unit tests; calculates length of the |
||||||
|
* longest collision chain. This should typically be a low number, |
||||||
|
* but may be up to {@link #size} - 1 in the pathological case |
||||||
|
* |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
public int maxCollisionLength() |
||||||
|
{ |
||||||
|
return _longestCollisionList; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Public API, accessing symbols: |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public String findSymbol(char[] buffer, int start, int len, int h) |
||||||
|
{ |
||||||
|
if (len < 1) { // empty Strings are simplest to handle up front
|
||||||
|
return ""; |
||||||
|
} |
||||||
|
if (!_canonicalize) { // [JACKSON-259]
|
||||||
|
return new String(buffer, start, len); |
||||||
|
} |
||||||
|
|
||||||
|
/* Related to problems with sub-standard hashing (somewhat |
||||||
|
* relevant for collision attacks too), let's try little |
||||||
|
* bit of shuffling to improve hash codes. |
||||||
|
* (note, however, that this can't help with full collisions) |
||||||
|
*/ |
||||||
|
int index = _hashToIndex(h); |
||||||
|
String sym = _symbols[index]; |
||||||
|
|
||||||
|
// Optimal case; checking existing primary symbol for hash index:
|
||||||
|
if (sym != null) { |
||||||
|
// Let's inline primary String equality checking:
|
||||||
|
if (sym.length() == len) { |
||||||
|
int i = 0; |
||||||
|
do { |
||||||
|
if (sym.charAt(i) != buffer[start+i]) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} while (++i < len); |
||||||
|
// Optimal case; primary match found
|
||||||
|
if (i == len) { |
||||||
|
return sym; |
||||||
|
} |
||||||
|
} |
||||||
|
// How about collision bucket?
|
||||||
|
Bucket b = _buckets[index >> 1]; |
||||||
|
if (b != null) { |
||||||
|
sym = b.find(buffer, start, len); |
||||||
|
if (sym != null) { |
||||||
|
return sym; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!_dirty) { //need to do copy-on-write?
|
||||||
|
copyArrays(); |
||||||
|
_dirty = true; |
||||||
|
} else if (_size >= _sizeThreshold) { // Need to expand?
|
||||||
|
rehash(); |
||||||
|
/* Need to recalc hash; rare occurence (index mask has been |
||||||
|
* recalculated as part of rehash) |
||||||
|
*/ |
||||||
|
index = _hashToIndex(calcHash(buffer, start, len)); |
||||||
|
} |
||||||
|
|
||||||
|
String newSymbol = new String(buffer, start, len); |
||||||
|
if (_intern) { |
||||||
|
newSymbol = InternCache.instance.intern(newSymbol); |
||||||
|
} |
||||||
|
++_size; |
||||||
|
// Ok; do we need to add primary entry, or a bucket?
|
||||||
|
if (_symbols[index] == null) { |
||||||
|
_symbols[index] = newSymbol; |
||||||
|
} else { |
||||||
|
int bix = (index >> 1); |
||||||
|
Bucket newB = new Bucket(newSymbol, _buckets[bix]); |
||||||
|
_buckets[bix] = newB; |
||||||
|
_longestCollisionList = Math.max(newB.length(), _longestCollisionList); |
||||||
|
if (_longestCollisionList > MAX_COLL_CHAIN_LENGTH) { |
||||||
|
reportTooManyCollisions(MAX_COLL_CHAIN_LENGTH); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return newSymbol; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper method that takes in a "raw" hash value, shuffles it as necessary, |
||||||
|
* and truncates to be used as the index. |
||||||
|
*/ |
||||||
|
public int _hashToIndex(int rawHash) |
||||||
|
{ |
||||||
|
rawHash += (rawHash >>> 15); // this seems to help quite a bit, at least for our tests
|
||||||
|
return (rawHash & _indexMask); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implementation of a hashing method for variable length |
||||||
|
* Strings. Most of the time intention is that this calculation |
||||||
|
* is done by caller during parsing, not here; however, sometimes |
||||||
|
* it needs to be done for parsed "String" too. |
||||||
|
* |
||||||
|
* @param len Length of String; has to be at least 1 (caller guarantees |
||||||
|
* this pre-condition) |
||||||
|
*/ |
||||||
|
public int calcHash(char[] buffer, int start, int len) |
||||||
|
{ |
||||||
|
int hash = _hashSeed; |
||||||
|
for (int i = 0; i < len; ++i) { |
||||||
|
hash = (hash * HASH_MULT) + (int) buffer[i]; |
||||||
|
} |
||||||
|
// NOTE: shuffling, if any, is done in 'findSymbol()', not here:
|
||||||
|
return (hash == 0) ? 1 : hash; |
||||||
|
} |
||||||
|
|
||||||
|
public int calcHash(String key) |
||||||
|
{ |
||||||
|
final int len = key.length(); |
||||||
|
|
||||||
|
int hash = _hashSeed; |
||||||
|
for (int i = 0; i < len; ++i) { |
||||||
|
hash = (hash * HASH_MULT) + (int) key.charAt(i); |
||||||
|
} |
||||||
|
// NOTE: shuffling, if any, is done in 'findSymbol()', not here:
|
||||||
|
return (hash == 0) ? 1 : hash; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Internal methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called when copy-on-write is needed; generally when first |
||||||
|
* change is made to a derived symbol table. |
||||||
|
*/ |
||||||
|
private void copyArrays() |
||||||
|
{ |
||||||
|
final String[] oldSyms = _symbols; |
||||||
|
_symbols = ArraysCompat.copyOf(oldSyms, oldSyms.length); |
||||||
|
final Bucket[] oldBuckets = _buckets; |
||||||
|
_buckets = ArraysCompat.copyOf(oldBuckets, oldBuckets.length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Method called when size (number of entries) of symbol table grows |
||||||
|
* so big that load factor is exceeded. Since size has to remain |
||||||
|
* power of two, arrays will then always be doubled. Main work |
||||||
|
* is really redistributing old entries into new String/Bucket |
||||||
|
* entries. |
||||||
|
*/ |
||||||
|
private void rehash() |
||||||
|
{ |
||||||
|
int size = _symbols.length; |
||||||
|
int newSize = size + size; |
||||||
|
|
||||||
|
/* 12-Mar-2010, tatu: Let's actually limit maximum size we are |
||||||
|
* prepared to use, to guard against OOME in case of unbounded |
||||||
|
* name sets (unique [non-repeating] names) |
||||||
|
*/ |
||||||
|
if (newSize > MAX_TABLE_SIZE) { |
||||||
|
/* If this happens, there's no point in either growing or |
||||||
|
* shrinking hash areas. Rather, it's better to just clean |
||||||
|
* them up for reuse. |
||||||
|
*/ |
||||||
|
_size = 0; |
||||||
|
Arrays.fill(_symbols, null); |
||||||
|
Arrays.fill(_buckets, null); |
||||||
|
_dirty = true; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
String[] oldSyms = _symbols; |
||||||
|
Bucket[] oldBuckets = _buckets; |
||||||
|
_symbols = new String[newSize]; |
||||||
|
_buckets = new Bucket[newSize >> 1]; |
||||||
|
// Let's update index mask, threshold, now (needed for rehashing)
|
||||||
|
_indexMask = newSize - 1; |
||||||
|
_sizeThreshold = _thresholdSize(newSize); |
||||||
|
|
||||||
|
int count = 0; // let's do sanity check
|
||||||
|
|
||||||
|
/* Need to do two loops, unfortunately, since spill-over area is |
||||||
|
* only half the size: |
||||||
|
*/ |
||||||
|
int maxColl = 0; |
||||||
|
for (int i = 0; i < size; ++i) { |
||||||
|
String symbol = oldSyms[i]; |
||||||
|
if (symbol != null) { |
||||||
|
++count; |
||||||
|
int index = _hashToIndex(calcHash(symbol)); |
||||||
|
if (_symbols[index] == null) { |
||||||
|
_symbols[index] = symbol; |
||||||
|
} else { |
||||||
|
int bix = (index >> 1); |
||||||
|
Bucket newB = new Bucket(symbol, _buckets[bix]); |
||||||
|
_buckets[bix] = newB; |
||||||
|
maxColl = Math.max(maxColl, newB.length()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size >>= 1; |
||||||
|
for (int i = 0; i < size; ++i) { |
||||||
|
Bucket b = oldBuckets[i]; |
||||||
|
while (b != null) { |
||||||
|
++count; |
||||||
|
String symbol = b.getSymbol(); |
||||||
|
int index = _hashToIndex(calcHash(symbol)); |
||||||
|
if (_symbols[index] == null) { |
||||||
|
_symbols[index] = symbol; |
||||||
|
} else { |
||||||
|
int bix = (index >> 1); |
||||||
|
Bucket newB = new Bucket(symbol, _buckets[bix]); |
||||||
|
_buckets[bix] = newB; |
||||||
|
maxColl = Math.max(maxColl, newB.length()); |
||||||
|
} |
||||||
|
b = b.getNext(); |
||||||
|
} |
||||||
|
} |
||||||
|
_longestCollisionList = maxColl; |
||||||
|
|
||||||
|
if (count != _size) { |
||||||
|
throw new Error("Internal error on SymbolTable.rehash(): had "+_size+" entries; now have "+count+"."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @since 2.1 |
||||||
|
*/ |
||||||
|
protected void reportTooManyCollisions(int maxLen) |
||||||
|
{ |
||||||
|
throw new IllegalStateException("Longest collision chain in symbol table (of size "+_size |
||||||
|
+") now exceeds maximum, "+maxLen+" -- suspect a DoS attack based on hash collisions"); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Bucket class
|
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is a symbol table entry. Each entry acts as a node |
||||||
|
* in a linked list. |
||||||
|
*/ |
||||||
|
static final class Bucket |
||||||
|
{ |
||||||
|
private final String _symbol; |
||||||
|
private final Bucket _next; |
||||||
|
private final int _length; |
||||||
|
|
||||||
|
public Bucket(String symbol, Bucket next) { |
||||||
|
_symbol = symbol; |
||||||
|
_next = next; |
||||||
|
_length = (next == null) ? 1 : next._length+1; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSymbol() { return _symbol; } |
||||||
|
public Bucket getNext() { return _next; } |
||||||
|
public int length() { return _length; } |
||||||
|
|
||||||
|
public String find(char[] buf, int start, int len) { |
||||||
|
String sym = _symbol; |
||||||
|
Bucket b = _next; |
||||||
|
|
||||||
|
while (true) { // Inlined equality comparison:
|
||||||
|
if (sym.length() == len) { |
||||||
|
int i = 0; |
||||||
|
do { |
||||||
|
if (sym.charAt(i) != buf[start+i]) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} while (++i < len); |
||||||
|
if (i == len) { |
||||||
|
return sym; |
||||||
|
} |
||||||
|
} |
||||||
|
if (b == null) { |
||||||
|
break; |
||||||
|
} |
||||||
|
sym = b.getSymbol(); |
||||||
|
b = b.getNext(); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package com.fr.third.fasterxml.jackson.core.sym; |
||||||
|
|
||||||
|
/** |
||||||
|
* Base class for tokenized names (key strings in objects) that have |
||||||
|
* been tokenized from byte-based input sources (like |
||||||
|
* {@link java.io.InputStream}. |
||||||
|
* |
||||||
|
* @author Tatu Saloranta |
||||||
|
*/ |
||||||
|
public abstract class Name |
||||||
|
{ |
||||||
|
protected final String _name; |
||||||
|
|
||||||
|
protected final int _hashCode; |
||||||
|
|
||||||
|
protected Name(String name, int hashCode) { |
||||||
|
_name = name; |
||||||
|
_hashCode = hashCode; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { return _name; } |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Methods for package/core parser |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
public abstract boolean equals(int quad1); |
||||||
|
|
||||||
|
public abstract boolean equals(int quad1, int quad2); |
||||||
|
|
||||||
|
public abstract boolean equals(int[] quads, int qlen); |
||||||
|
|
||||||
|
/* |
||||||
|
/********************************************************** |
||||||
|
/* Overridden standard methods |
||||||
|
/********************************************************** |
||||||
|
*/ |
||||||
|
|
||||||
|
@Override public String toString() { return _name; } |
||||||
|
|
||||||
|
@Override public final int hashCode() { return _hashCode; } |
||||||
|
|
||||||
|
@Override public boolean equals(Object o) |
||||||
|
{ |
||||||
|
// Canonical instances, can usually just do identity comparison
|
||||||
|
return (o == this); |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue