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      */

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      */

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      */

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      */

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      */

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      */

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      */

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          * (truefalse) should be used.
211          */

212         BOOLEAN,
213
214         /**
215          * Value that indicates that Binary type (nativeif 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
223         public boolean isNumeric() {
224             return (this == NUMBER) || (this == NUMBER_INT) || (this == NUMBER_FLOAT);
225         }
226
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         
328         private Features(int e, int d) {
329             _enabled = e;
330             _disabled = d;
331         }
332
333         public static Features empty() {
334             return EMPTY;
335         }
336         
337         public static Features construct(JsonFormat f) {
338             return construct(f.with(), f.without());
339         }
340         
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
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
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
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
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
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
415         public int hashCode() {
416             return _disabled + _enabled;
417         }
418
419         @Override
420         public boolean equals(Object o) {
421             if (o == thisreturn true;
422             if (o == nullreturn 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         
460         public Value() {
461             this("", Shape.ANY, """", Features.empty(), null);
462         }
463         
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          */

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          */

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          */

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
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
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
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          */

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          */

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          */

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          */

568         public final static Value from(JsonFormat ann) {
569             return (ann == null) ? EMPTY : new Value(ann);
570         }
571
572         /**
573          * @since 2.7
574          */

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          */

621         public static Value forPattern(String p) {
622             return new Value(p, nullnullnullnull, Features.empty(), null);
623         }
624
625         /**
626          * @since 2.7
627          */

628         public static Value forShape(Shape sh) {
629             return new Value("", sh, nullnullnull, Features.empty(), null);
630         }
631
632         /**
633          * @since 2.9
634          */

635         public static Value forLeniency(boolean lenient) {
636             return new Value(""nullnullnullnull, Features.empty(),
637                     Boolean.valueOf(lenient));
638         }
639
640         /**
641          * @since 2.1
642          */

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          */

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          */

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          */

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          */

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          */

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          */

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
707         public Class<JsonFormat> valueFor() {
708             return JsonFormat.class;
709         }
710         
711         public String getPattern() { return _pattern; }
712         public Shape getShape() { return _shape; }
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 nullif not set either way (assuming
718          *   "default leniency" for the context)
719          *
720          * @since 2.9
721          */

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 trueif (and only if) leniency has been explicitly
732          * set to {code true}; but not if it is undefined.
733          *
734          * @since 2.9
735          */

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          */

747         public String timeZoneAsString() {
748             if (_timezone != null) {
749                 return _timezone.getID();
750             }
751             return _timezoneStr;
752         }
753         
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          */

769         public boolean hasShape() { return _shape != Shape.ANY; }
770         
771         /**
772          * @since 2.4
773          */

774         public boolean hasPattern() {
775             return (_pattern != null) && (_pattern.length() > 0);
776         }
777         
778         /**
779          * @since 2.4
780          */

781         public boolean hasLocale() { return _locale != null; }
782
783         /**
784          * @since 2.4
785          */

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          */

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          */

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          */

819         public Features getFeatures() {
820             return _features;
821         }
822
823         @Override
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
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
847         public boolean equals(Object o) {
848             if (o == thisreturn true;
849             if (o == nullreturn 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
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