• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.annotation;
2 
3 import java.lang.annotation.*;
4 import java.util.Locale;
5 import java.util.TimeZone;
6 
7 /**
8  * General-purpose annotation used for configuring details of how
9  * values of properties are to be serialized.
10  * Unlike most other Jackson annotations, annotation does not
11  * have specific universal interpretation: instead, effect depends on datatype
12  * of property being annotated (or more specifically, deserializer
13  * and serializer being used).
14  *<p>
15  * Common uses include choosing between alternate representations -- for example,
16  * whether {@link java.util.Date} is to be serialized as number (Java timestamp)
17  * or String (such as ISO-8601 compatible time value) -- as well as configuring
18  * exact details with {@link #pattern} property.
19  *<p>
20  * As of Jackson 2.6, known special handling includes:
21  *<ul>
22  * <li>{@link java.util.Date}: Shape can  be {@link Shape#STRING} or {@link Shape#NUMBER};
23  *    pattern may contain {@link java.text.SimpleDateFormat}-compatible pattern definition.
24  *   </li>
25  * <li>Can be used on Classes (types) as well, for modified default behavior, possibly
26  *   overridden by per-property annotation
27  *   </li>
28  * <li>{@link java.lang.Enum}s: Shapes {@link Shape#STRING} and {@link Shape#NUMBER} can be
29  *    used to change between numeric (index) and textual (name or <code>toString()</code>);
30  *    but it is also possible to use {@link Shape#OBJECT} to serialize (but not deserialize)
31  *    {@link java.lang.Enum}s as JSON Objects (as if they were POJOs). NOTE: serialization
32  *     as JSON Object only works with class annotation;
33  *    will not work as per-property annotation.
34  *   </li>
35  * <li>{@link java.util.Collection}s can be serialized as (and deserialized from) JSON Objects,
36  *    if {@link Shape#OBJECT} is used. NOTE: can ONLY be used as class annotation;
37  *    will not work as per-property annotation.
38  *   </li>
39  * <li>{@link java.lang.Number} subclasses can be serialized as full objects if
40  *    {@link Shape#OBJECT} is used. Otherwise the default behavior of serializing to a
41  *    scalar number value will be preferred. NOTE: can ONLY be used as class annotation;
42  *    will not work as per-property annotation.
43  *   </li>
44  *</ul>
45  *
46  * @since 2.0
47  */
48 @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
49     ElementType.TYPE})
50 @Retention(RetentionPolicy.RUNTIME)
51 @JacksonAnnotation
52 public @interface JsonFormat
53 {
54     /**
55      * Value that indicates that default {@link java.util.Locale}
56      * (from deserialization or serialization context) should be used:
57      * annotation does not define value to use.
58      */
59     public final static String DEFAULT_LOCALE = "##default";
60 
61     /**
62      * Value that indicates that default {@link java.util.TimeZone}
63      * (from deserialization or serialization context) should be used:
64      * annotation does not define value to use.
65      *<p>
66      * NOTE: default here does NOT mean JVM defaults but Jackson databindings
67      * default, usually UTC, but may be changed on <code>ObjectMapper</code>.
68      */
69     public final static String DEFAULT_TIMEZONE = "##default";
70 
71     /**
72      * Datatype-specific additional piece of configuration that may be used
73      * to further refine formatting aspects. This may, for example, determine
74      * low-level format String used for {@link java.util.Date} serialization;
75      * however, exact use is determined by specific <code>JsonSerializer</code>
76      */
pattern()77     public String pattern() default "";
78 
79     /**
80      * Structure to use for serialization: definition of mapping depends on datatype,
81      * but usually has straight-forward counterpart in data format (JSON).
82      * Note that commonly only a subset of shapes is available; and if 'invalid' value
83      * is chosen, defaults are usually used.
84      */
shape()85     public Shape shape() default Shape.ANY;
86 
87     /**
88      * {@link java.util.Locale} to use for serialization (if needed).
89      * Special value of {@link #DEFAULT_LOCALE}
90      * can be used to mean "just use the default", where default is specified
91      * by the serialization context, which in turn defaults to system
92      * defaults ({@link java.util.Locale#getDefault()}) unless explicitly
93      * set to another locale.
94      */
locale()95     public String locale() default DEFAULT_LOCALE;
96 
97     /**
98      * {@link java.util.TimeZone} to use for serialization (if needed).
99      * Special value of {@link #DEFAULT_TIMEZONE}
100      * can be used to mean "just use the default", where default is specified
101      * by the serialization context, which in turn defaults to system
102      * default (UTC) unless explicitly set to another timezone.
103      */
timezone()104     public String timezone() default DEFAULT_TIMEZONE;
105 
106     /**
107      * Property that indicates whether "lenient" handling should be enabled or
108      * disabled. This is relevant mostly for deserialization of some textual
109      * datatypes, especially date/time types.
110      *<p>
111      * Note that underlying default setting depends on datatype (or more precisely
112      * deserializer for it): for most date/time types, default is for leniency
113      * to be enabled.
114      *
115      * @since 2.9
116      */
lenient()117     public OptBoolean lenient() default OptBoolean.DEFAULT;
118 
119     /**
120      * Set of {@link JsonFormat.Feature}s to explicitly enable with respect
121      * to handling of annotated property. This will have precedence over possible
122      * global configuration.
123      *
124      * @since 2.6
125      */
with()126     public JsonFormat.Feature[] with() default { };
127 
128     /**
129      * Set of {@link JsonFormat.Feature}s to explicitly disable with respect
130      * to handling of annotated property. This will have precedence over possible
131      * global configuration.
132      *
133      * @since 2.6
134      */
without()135     public JsonFormat.Feature[] without() default { };
136 
137     /*
138     /**********************************************************
139     /* Value enumeration(s), value class(es)
140     /**********************************************************
141      */
142 
143     /**
144      * Value enumeration used for indicating preferred Shape; translates
145      * loosely to JSON types, with some extra values to indicate less precise
146      * choices (i.e. allowing one of multiple actual shapes)
147      */
148     public enum Shape
149     {
150         /**
151          * Marker enum value that indicates "whatever" choice, meaning that annotation
152          * does NOT specify shape to use.
153          * Note that this is different from {@link Shape#NATURAL}, which
154          * specifically instructs use of the "natural" shape for datatype.
155          */
156         ANY,
157 
158         /**
159          * Marker enum value that indicates the "default" choice for given datatype;
160          * for example, JSON String for {@link java.lang.String}, or JSON Number
161          * for Java numbers.
162          * Note that this is different from {@link Shape#ANY} in that this is actual
163          * explicit choice that overrides possible default settings.
164          *
165          * @since 2.8
166          */
167         NATURAL,
168 
169         /**
170          * Value that indicates shape should not be structural (that is, not
171          * {@link #ARRAY} or {@link #OBJECT}, but can be any other shape.
172          */
173         SCALAR,
174 
175         /**
176          * Value that indicates that (JSON) Array type should be used.
177          */
178         ARRAY,
179 
180         /**
181          * Value that indicates that (JSON) Object type should be used.
182          */
183         OBJECT,
184 
185         /**
186          * Value that indicates that a numeric (JSON) type should be used
187          * (but does not specify whether integer or floating-point representation
188          * should be used)
189          */
190         NUMBER,
191 
192         /**
193          * Value that indicates that floating-point numeric type should be used
194          */
195         NUMBER_FLOAT,
196 
197         /**
198          * Value that indicates that integer number type should be used
199          * (and not {@link #NUMBER_FLOAT}).
200          */
201         NUMBER_INT,
202 
203         /**
204          * Value that indicates that (JSON) String type should be used.
205          */
206         STRING,
207 
208         /**
209          * Value that indicates that (JSON) boolean type
210          * (true, false) should be used.
211          */
212         BOOLEAN,
213 
214         /**
215          * Value that indicates that Binary type (native, if format supports it;
216          * encoding using Base64 if only textual types supported) should be used.
217          *
218          * @since 2.10
219          */
220         BINARY
221         ;
222 
isNumeric()223         public boolean isNumeric() {
224             return (this == NUMBER) || (this == NUMBER_INT) || (this == NUMBER_FLOAT);
225         }
226 
isStructured()227         public boolean isStructured() {
228             return (this == OBJECT) || (this == ARRAY);
229         }
230     }
231 
232     /**
233      * Set of features that can be enabled/disabled for property annotated.
234      * These often relate to specific <code>SerializationFeature</code>
235      * or <code>DeserializationFeature</code>, as noted by entries.
236      *<p>
237      * Note that whether specific setting has an effect depends on whether
238      * <code>JsonSerializer</code> / <code>JsonDeserializer</code> being used
239      * takes the format setting into account. If not, please file an issue
240      * for adding support via issue tracker for package that has handlers
241      * (if you know which one; if not, just use `jackson-databind`).
242      *
243      * @since 2.6
244      */
245     public enum Feature {
246         /**
247          * Override for <code>DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY</code>
248          * which will allow deserialization of JSON non-array values into single-element
249          * Java arrays and {@link java.util.Collection}s.
250          */
251         ACCEPT_SINGLE_VALUE_AS_ARRAY,
252 
253         /**
254          * Override for <code>MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES</code>,
255          * which allows case-insensitive matching of property names (but NOT values,
256          * see {@link #ACCEPT_CASE_INSENSITIVE_VALUES} for that).
257          *<p>
258          * Only affects deserialization, has no effect on serialization.
259          *
260          * @since 2.8
261          */
262         ACCEPT_CASE_INSENSITIVE_PROPERTIES,
263 
264         /**
265          * Override for <code>MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES</code>,
266          * which allows case-sensitive matching of (some) property values, such
267          * as {@code Enum}s.
268          * Only affects deserialization, has no effect on serialization.
269          *
270          * @since 2.10
271          */
272         ACCEPT_CASE_INSENSITIVE_VALUES,
273 
274         /**
275          * Override for <code>SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS</code>,
276          * similar constraints apply.
277          */
278         WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS,
279 
280         /**
281          * Override for <code>SerializationFeature.WRITE_DATES_WITH_ZONE_ID</code>,
282          * similar constraints apply.
283          */
284         WRITE_DATES_WITH_ZONE_ID,
285 
286         /**
287          * Override for <code>SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED</code>
288          * which will force serialization of single-element arrays and {@link java.util.Collection}s
289          * as that single element and excluding array wrapper.
290          */
291         WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,
292 
293         /**
294          * Override for <code>SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS</code>,
295          * enabling of which will force sorting of {@link java.util.Map} keys before
296          * serialization.
297          */
298         WRITE_SORTED_MAP_ENTRIES,
299 
300         /**
301          * Override for <code>DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIMEZONE</code>
302          * that specifies whether context provided timezone
303          * <code>DeserializationContext.getTimeZone()</code> should be used to adjust Date/Time
304          * values on deserialization, even if value itself contains timezone informatio
305          *<p>
306          * NOTE: due to limitations of "old" JDK date/time types (that is,
307          * {@link java.util.Date} and {@link java.util.Calendar}), this setting is only
308          * applicable to <code>Joda</code> and <code>Java 8 date/time</code> values,
309          * but not to <code>java.util.Date</code> or <code>java.util.Calendar</code>.
310          *
311          * @since 2.8
312          */
313         ADJUST_DATES_TO_CONTEXT_TIME_ZONE
314     }
315 
316     /**
317      * Helper class that encapsulates information equivalent to {@link java.lang.Boolean}
318      * valued {@link java.util.EnumMap}.
319      *
320      * @since 2.6
321      */
322     public static class Features
323     {
324         private final int _enabled, _disabled;
325 
326         private final static Features EMPTY = new Features(0, 0);
327 
Features(int e, int d)328         private Features(int e, int d) {
329             _enabled = e;
330             _disabled = d;
331         }
332 
empty()333         public static Features empty() {
334             return EMPTY;
335         }
336 
construct(JsonFormat f)337         public static Features construct(JsonFormat f) {
338             return construct(f.with(), f.without());
339         }
340 
construct(Feature[] enabled, Feature[] disabled)341         public static Features construct(Feature[] enabled, Feature[] disabled)
342         {
343             int e = 0;
344             for (Feature f : enabled) {
345                 e |= (1 << f.ordinal());
346             }
347             int d = 0;
348             for (Feature f : disabled) {
349                 d |= (1 << f.ordinal());
350             }
351             return new Features(e, d);
352         }
353 
withOverrides(Features overrides)354         public Features withOverrides(Features overrides) {
355             // Cheap checks first: maybe one is empty?
356             if (overrides == null) {
357                 return this;
358             }
359             int overrideD = overrides._disabled;
360             int overrideE = overrides._enabled;
361             if ((overrideD == 0) && (overrideE == 0)) {
362                 return this;
363             }
364             if ((_enabled == 0) && (_disabled == 0)) {
365                 return overrides;
366             }
367             // If not, calculate combination with overrides
368             int newE = (_enabled & ~overrideD) | overrideE;
369             int newD = (_disabled & ~overrideE) | overrideD;
370 
371             // one more thing; no point in creating new instance if there's no change
372             if ((newE == _enabled) && (newD == _disabled)) {
373                 return this;
374             }
375 
376             return new Features(newE, newD);
377         }
378 
with(Feature...features)379         public Features with(Feature...features) {
380             int e = _enabled;
381             for (Feature f : features) {
382                 e |= (1 << f.ordinal());
383             }
384             return (e == _enabled) ? this : new Features(e, _disabled);
385         }
386 
without(Feature...features)387         public Features without(Feature...features) {
388             int d = _disabled;
389             for (Feature f : features) {
390                 d |= (1 << f.ordinal());
391             }
392             return (d == _disabled) ? this : new Features(_enabled, d);
393         }
394 
get(Feature f)395         public Boolean get(Feature f) {
396             int mask = (1 << f.ordinal());
397             if ((_disabled & mask) != 0) {
398                 return Boolean.FALSE;
399             }
400             if ((_enabled & mask) != 0) {
401                 return Boolean.TRUE;
402             }
403             return null;
404         }
405 
406         @Override
toString()407         public String toString() {
408             if (this == EMPTY) {
409                 return "EMPTY";
410             }
411             return String.format("(enabled=0x%x,disabled=0x%x)", _enabled, _disabled);
412         }
413 
414         @Override
hashCode()415         public int hashCode() {
416             return _disabled + _enabled;
417         }
418 
419         @Override
equals(Object o)420         public boolean equals(Object o) {
421             if (o == this) return true;
422             if (o == null) return false;
423             if (o.getClass() != getClass()) return false;
424             Features other = (Features) o;
425             return (other._enabled == _enabled) && (other._disabled == _disabled);
426         }
427     }
428 
429     /**
430      * Helper class used to contain information from a single {@link JsonFormat}
431      * annotation.
432      */
433     public static class Value
434         implements JacksonAnnotationValue<JsonFormat>, // since 2.6
435             java.io.Serializable
436     {
437         private static final long serialVersionUID = 1L;
438 
439         private final static Value EMPTY = new Value();
440 
441         private final String _pattern;
442         private final Shape _shape;
443         private final Locale _locale;
444 
445         private final String _timezoneStr;
446 
447         /**
448          * @since 2.9
449          */
450         private final Boolean _lenient;
451 
452         /**
453          * @since 2.6
454          */
455         private final Features _features;
456 
457         // lazily constructed when created from annotations
458         private transient TimeZone _timezone;
459 
Value()460         public Value() {
461             this("", Shape.ANY, "", "", Features.empty(), null);
462         }
463 
Value(JsonFormat ann)464         public Value(JsonFormat ann) {
465             this(ann.pattern(), ann.shape(), ann.locale(), ann.timezone(),
466                     Features.construct(ann), ann.lenient().asBoolean());
467         }
468 
469         /**
470          * @since 2.9
471          */
Value(String p, Shape sh, String localeStr, String tzStr, Features f, Boolean lenient)472         public Value(String p, Shape sh, String localeStr, String tzStr, Features f,
473                 Boolean lenient)
474         {
475             this(p, sh,
476                     (localeStr == null || localeStr.length() == 0 || DEFAULT_LOCALE.equals(localeStr)) ?
477                             null : new Locale(localeStr),
478                     (tzStr == null || tzStr.length() == 0 || DEFAULT_TIMEZONE.equals(tzStr)) ?
479                             null : tzStr,
480                     null, f, lenient);
481         }
482 
483         /**
484          * @since 2.9
485          */
Value(String p, Shape sh, Locale l, TimeZone tz, Features f, Boolean lenient)486         public Value(String p, Shape sh, Locale l, TimeZone tz, Features f,
487                 Boolean lenient)
488         {
489             _pattern = (p == null) ? "" : p;
490             _shape = (sh == null) ? Shape.ANY : sh;
491             _locale = l;
492             _timezone = tz;
493             _timezoneStr = null;
494             _features = (f == null) ? Features.empty() : f;
495             _lenient = lenient;
496         }
497 
498         /**
499          * @since 2.9
500          */
Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f, Boolean lenient)501         public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f,
502                 Boolean lenient)
503         {
504             _pattern = (p == null) ? "" : p;
505             _shape = (sh == null) ? Shape.ANY : sh;
506             _locale = l;
507             _timezone = tz;
508             _timezoneStr = tzStr;
509             _features = (f == null) ? Features.empty() : f;
510             _lenient = lenient;
511         }
512 
513         @Deprecated // since 2.9
Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f)514         public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f) {
515             this(p, sh, l, tzStr, tz, f, null);
516         }
517 
518         @Deprecated // since 2.9
Value(String p, Shape sh, String localeStr, String tzStr, Features f)519         public Value(String p, Shape sh, String localeStr, String tzStr, Features f) {
520             this(p, sh, localeStr, tzStr, f, null);
521         }
522         @Deprecated // since 2.9
Value(String p, Shape sh, Locale l, TimeZone tz, Features f)523         public Value(String p, Shape sh, Locale l, TimeZone tz, Features f) {
524             this(p, sh, l, tz, f, null);
525         }
526 
527         /**
528          * @since 2.7
529          */
empty()530         public final static Value empty() {
531             return EMPTY;
532         }
533 
534         /**
535          * Helper method that will try to combine values from two {@link Value}
536          * instances, using one as base settings, and the other as overrides
537          * to use instead of base values when defined; base values are only
538          * use if override does not specify a value (matching value is null
539          * or logically missing).
540          * Note that one or both of value instances may be `null`, directly;
541          * if both are `null`, result will also be `null`; otherwise never null.
542          *
543          * @since 2.8
544          */
merge(Value base, Value overrides)545         public static Value merge(Value base, Value overrides)
546         {
547             return (base == null) ? overrides
548                     : base.withOverrides(overrides);
549         }
550 
551         /**
552          * @since 2.8
553          */
mergeAll(Value... values)554         public static Value mergeAll(Value... values)
555         {
556             Value result = null;
557             for (Value curr : values) {
558                 if (curr != null) {
559                     result = (result == null)  ? curr : result.withOverrides(curr);
560                 }
561             }
562             return result;
563         }
564 
565         /**
566          * @since 2.7
567          */
from(JsonFormat ann)568         public final static Value from(JsonFormat ann) {
569             return (ann == null) ? EMPTY : new Value(ann);
570         }
571 
572         /**
573          * @since 2.7
574          */
withOverrides(Value overrides)575         public final Value withOverrides(Value overrides) {
576             if ((overrides == null) || (overrides == EMPTY) || (overrides == this)) {
577                 return this;
578             }
579             if (this == EMPTY) { // cheesy, but probably common enough
580                 return overrides;
581             }
582             String p = overrides._pattern;
583             if ((p == null) || p.isEmpty()) {
584                 p = _pattern;
585             }
586             Shape sh = overrides._shape;
587             if (sh == Shape.ANY) {
588                 sh = _shape;
589             }
590             Locale l = overrides._locale;
591             if (l == null) {
592                 l = _locale;
593             }
594             Features f = _features;
595             if (f == null) {
596                 f = overrides._features;
597             } else {
598                 f = f.withOverrides(overrides._features);
599             }
600             Boolean lenient = overrides._lenient;
601             if (lenient == null) {
602                 lenient = _lenient;
603             }
604 
605             // timezone not merged, just choose one
606             String tzStr = overrides._timezoneStr;
607             TimeZone tz;
608 
609             if ((tzStr == null) || tzStr.isEmpty()) { // no overrides, use space
610                 tzStr = _timezoneStr;
611                 tz = _timezone;
612             } else {
613                 tz = overrides._timezone;
614             }
615             return new Value(p, sh, l, tzStr, tz, f, lenient);
616         }
617 
618         /**
619          * @since 2.6
620          */
forPattern(String p)621         public static Value forPattern(String p) {
622             return new Value(p, null, null, null, null, Features.empty(), null);
623         }
624 
625         /**
626          * @since 2.7
627          */
forShape(Shape sh)628         public static Value forShape(Shape sh) {
629             return new Value("", sh, null, null, null, Features.empty(), null);
630         }
631 
632         /**
633          * @since 2.9
634          */
forLeniency(boolean lenient)635         public static Value forLeniency(boolean lenient) {
636             return new Value("", null, null, null, null, Features.empty(),
637                     Boolean.valueOf(lenient));
638         }
639 
640         /**
641          * @since 2.1
642          */
withPattern(String p)643         public Value withPattern(String p) {
644             return new Value(p, _shape, _locale, _timezoneStr, _timezone,
645                     _features, _lenient);
646         }
647 
648         /**
649          * @since 2.1
650          */
withShape(Shape s)651         public Value withShape(Shape s) {
652             if (s == _shape) {
653                 return this;
654             }
655             return new Value(_pattern, s, _locale, _timezoneStr, _timezone,
656                     _features, _lenient);
657         }
658 
659         /**
660          * @since 2.1
661          */
withLocale(Locale l)662         public Value withLocale(Locale l) {
663             return new Value(_pattern, _shape, l, _timezoneStr, _timezone,
664                     _features, _lenient);
665         }
666 
667         /**
668          * @since 2.1
669          */
withTimeZone(TimeZone tz)670         public Value withTimeZone(TimeZone tz) {
671             return new Value(_pattern, _shape, _locale, null, tz,
672                     _features, _lenient);
673         }
674 
675         /**
676          * @since 2.9
677          */
withLenient(Boolean lenient)678         public Value withLenient(Boolean lenient) {
679             if (lenient == _lenient) {
680                 return this;
681             }
682             return new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
683                     _features, lenient);
684         }
685 
686         /**
687          * @since 2.6
688          */
withFeature(JsonFormat.Feature f)689         public Value withFeature(JsonFormat.Feature f) {
690             Features newFeats = _features.with(f);
691             return (newFeats == _features) ? this :
692                 new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
693                         newFeats, _lenient);
694         }
695 
696         /**
697          * @since 2.6
698          */
withoutFeature(JsonFormat.Feature f)699         public Value withoutFeature(JsonFormat.Feature f) {
700             Features newFeats = _features.without(f);
701             return (newFeats == _features) ? this :
702                 new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
703                         newFeats, _lenient);
704         }
705 
706         @Override
valueFor()707         public Class<JsonFormat> valueFor() {
708             return JsonFormat.class;
709         }
710 
getPattern()711         public String getPattern() { return _pattern; }
getShape()712         public Shape getShape() { return _shape; }
getLocale()713         public Locale getLocale() { return _locale; }
714 
715         /**
716          * @return {@code Boolean.TRUE} if explicitly set to true; {@code Boolean.FALSE}
717          *   if explicit set to false; or {@code null} if not set either way (assuming
718          *   "default leniency" for the context)
719          *
720          * @since 2.9
721          */
getLenient()722         public Boolean getLenient() {
723             return _lenient;
724         }
725 
726         /**
727          * Convenience method equivalent to
728          *<pre>
729          *   Boolean.TRUE.equals(getLenient())
730          *</pre>
731          * that is, returns {@code true} if (and only if) leniency has been explicitly
732          * set to {code true}; but not if it is undefined.
733          *
734          * @since 2.9
735          */
isLenient()736         public boolean isLenient() {
737             return Boolean.TRUE.equals(_lenient);
738         }
739 
740         /**
741          * Alternate access (compared to {@link #getTimeZone()}) which is useful
742          * when caller just wants time zone id to convert, but not as JDK
743          * provided {@link TimeZone}
744          *
745          * @since 2.4
746          */
timeZoneAsString()747         public String timeZoneAsString() {
748             if (_timezone != null) {
749                 return _timezone.getID();
750             }
751             return _timezoneStr;
752         }
753 
getTimeZone()754         public TimeZone getTimeZone() {
755             TimeZone tz = _timezone;
756             if (tz == null) {
757                 if (_timezoneStr == null) {
758                     return null;
759                 }
760                 tz = TimeZone.getTimeZone(_timezoneStr);
761                 _timezone = tz;
762             }
763             return tz;
764         }
765 
766         /**
767          * @since 2.4
768          */
hasShape()769         public boolean hasShape() { return _shape != Shape.ANY; }
770 
771         /**
772          * @since 2.4
773          */
hasPattern()774         public boolean hasPattern() {
775             return (_pattern != null) && (_pattern.length() > 0);
776         }
777 
778         /**
779          * @since 2.4
780          */
hasLocale()781         public boolean hasLocale() { return _locale != null; }
782 
783         /**
784          * @since 2.4
785          */
hasTimeZone()786         public boolean hasTimeZone() {
787             return (_timezone != null) || (_timezoneStr != null && !_timezoneStr.isEmpty());
788         }
789 
790         /**
791          * Accessor for checking whether there is a setting for leniency.
792          * NOTE: does NOT mean that `lenient` is `true` necessarily; just that
793          * it has been set.
794          *
795          * @since 2.9
796          */
hasLenient()797         public boolean hasLenient() {
798             return _lenient != null;
799         }
800 
801         /**
802          * Accessor for checking whether this format value has specific setting for
803          * given feature. Result is 3-valued with either `null`, {@link Boolean#TRUE} or
804          * {@link Boolean#FALSE}, indicating 'yes/no/dunno' choices, where `null` ("dunno")
805          * indicates that the default handling should be used based on global defaults,
806          * and there is no format override.
807          *
808          * @since 2.6
809          */
getFeature(JsonFormat.Feature f)810         public Boolean getFeature(JsonFormat.Feature f) {
811             return _features.get(f);
812         }
813 
814         /**
815          * Accessor for getting full set of features enabled/disabled.
816          *
817          * @since 2.8
818          */
getFeatures()819         public Features getFeatures() {
820             return _features;
821         }
822 
823         @Override
toString()824         public String toString() {
825             return String.format("JsonFormat.Value(pattern=%s,shape=%s,lenient=%s,locale=%s,timezone=%s,features=%s)",
826                     _pattern, _shape, _lenient, _locale, _timezoneStr, _features);
827         }
828 
829         @Override
hashCode()830         public int hashCode() {
831              int hash = (_timezoneStr == null) ? 1 : _timezoneStr.hashCode();
832              if (_pattern != null) {
833                  hash ^= _pattern.hashCode();
834              }
835              hash += _shape.hashCode();
836              if (_lenient != null) {
837                  hash ^= _lenient.hashCode();
838              }
839              if (_locale != null) {
840                  hash += _locale.hashCode();
841              }
842              hash ^= _features.hashCode();
843              return hash;
844         }
845 
846         @Override
equals(Object o)847         public boolean equals(Object o) {
848             if (o == this) return true;
849             if (o == null) return false;
850             if (o.getClass() != getClass()) return false;
851             Value other = (Value) o;
852 
853             if ((_shape != other._shape)
854                     || !_features.equals(other._features)) {
855                 return false;
856             }
857             return _equal(_lenient, other._lenient)
858                     && _equal(_timezoneStr, other._timezoneStr)
859                     && _equal(_pattern, other._pattern)
860                     && _equal(_timezone, other._timezone)
861                     && _equal(_locale, other._locale);
862         }
863 
_equal(T value1, T value2)864         private static <T> boolean _equal(T value1, T value2)
865         {
866             if (value1 == null) {
867                 return (value2 == null);
868             }
869             if (value2 == null) {
870                 return false;
871             }
872             return value1.equals(value2);
873         }
874     }
875 }
876