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