001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.isis.viewer.restfulobjects.applib; 020 021import java.io.InputStream; 022import java.lang.reflect.Constructor; 023import java.math.BigDecimal; 024import java.math.BigInteger; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030import java.util.Map.Entry; 031 032import org.apache.isis.viewer.restfulobjects.applib.util.JsonNodeUtils; 033import org.apache.isis.viewer.restfulobjects.applib.util.PathNode; 034import org.apache.isis.viewer.restfulobjects.applib.util.UrlEncodingUtils; 035 036import org.codehaus.jackson.JsonNode; 037import org.codehaus.jackson.node.ArrayNode; 038import org.codehaus.jackson.node.BigIntegerNode; 039import org.codehaus.jackson.node.DecimalNode; 040import org.codehaus.jackson.node.JsonNodeFactory; 041import org.codehaus.jackson.node.NullNode; 042import org.codehaus.jackson.node.ObjectNode; 043import org.codehaus.jackson.node.POJONode; 044import org.joda.time.format.DateTimeFormat; 045import org.joda.time.format.DateTimeFormatter; 046 047import com.google.common.base.Function; 048import com.google.common.base.Predicate; 049import com.google.common.collect.Iterables; 050import com.google.common.collect.Iterators; 051import com.google.common.collect.Lists; 052import com.google.common.collect.Maps; 053 054/** 055 * A wrapper around {@link JsonNode} that provides some additional helper 056 * methods. 057 */ 058public class JsonRepresentation { 059 060 public interface HasLinkToSelf { 061 public LinkRepresentation getSelf(); 062 } 063 064 public interface HasLinkToUp { 065 public LinkRepresentation getUp(); 066 } 067 068 public interface HasLinks { 069 public JsonRepresentation getLinks(); 070 } 071 072 public interface HasExtensions { 073 public JsonRepresentation getExtensions(); 074 } 075 076 private static Map<Class<?>, Function<JsonNode, ?>> REPRESENTATION_INSTANTIATORS = Maps.newHashMap(); 077 static { 078 REPRESENTATION_INSTANTIATORS.put(String.class, new Function<JsonNode, String>() { 079 @Override 080 public String apply(final JsonNode input) { 081 if (!input.isTextual()) { 082 throw new IllegalStateException("found node that is not a string " + input.toString()); 083 } 084 return input.getTextValue(); 085 } 086 }); 087 REPRESENTATION_INSTANTIATORS.put(JsonNode.class, new Function<JsonNode, JsonNode>() { 088 @Override 089 public JsonNode apply(final JsonNode input) { 090 return input; 091 } 092 }); 093 } 094 095 private static <T> Function<JsonNode, ?> representationInstantiatorFor(final Class<T> representationType) { 096 Function<JsonNode, ?> transformer = REPRESENTATION_INSTANTIATORS.get(representationType); 097 if (transformer == null) { 098 transformer = new Function<JsonNode, T>() { 099 @Override 100 public T apply(final JsonNode input) { 101 try { 102 final Constructor<T> constructor = representationType.getConstructor(JsonNode.class); 103 return constructor.newInstance(input); 104 } catch (final Exception e) { 105 throw new IllegalArgumentException("Conversions from JsonNode to " + representationType + " are not supported"); 106 } 107 } 108 109 }; 110 REPRESENTATION_INSTANTIATORS.put(representationType, transformer); 111 } 112 return transformer; 113 } 114 115 public static JsonRepresentation newMap(final String... keyValuePairs) { 116 final JsonRepresentation repr = new JsonRepresentation(new ObjectNode(JsonNodeFactory.instance)); 117 String key = null; 118 for (final String keyOrValue : keyValuePairs) { 119 if (key != null) { 120 repr.mapPut(key, keyOrValue); 121 key = null; 122 } else { 123 key = keyOrValue; 124 } 125 } 126 if (key != null) { 127 throw new IllegalArgumentException("must provide an even number of keys and values"); 128 } 129 return repr; 130 } 131 132 public static JsonRepresentation newArray() { 133 return newArray(0); 134 } 135 136 public static JsonRepresentation newArray(final int initialSize) { 137 final ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance); 138 for (int i = 0; i < initialSize; i++) { 139 arrayNode.addNull(); 140 } 141 return new JsonRepresentation(arrayNode); 142 } 143 144 protected final JsonNode jsonNode; 145 146 public JsonRepresentation(final JsonNode jsonNode) { 147 this.jsonNode = jsonNode; 148 } 149 150 public JsonNode asJsonNode() { 151 return jsonNode; 152 } 153 154 public int size() { 155 if (!isMap() && !isArray()) { 156 throw new IllegalStateException("not a map or an array"); 157 } 158 return jsonNode.size(); 159 } 160 161 /** 162 * Node is a value (nb: could be {@link #isNull() null}). 163 */ 164 public boolean isValue() { 165 return jsonNode.isValueNode(); 166 } 167 168 // /////////////////////////////////////////////////////////////////////// 169 // getRepresentation 170 // /////////////////////////////////////////////////////////////////////// 171 172 public JsonRepresentation getRepresentation(final String pathTemplate, final Object... args) { 173 final String pathStr = String.format(pathTemplate, args); 174 175 final JsonNode node = getNode(pathStr); 176 177 if (representsNull(node)) { 178 return null; 179 } 180 181 return new JsonRepresentation(node); 182 } 183 184 // /////////////////////////////////////////////////////////////////////// 185 // isArray, getArray, asArray 186 // /////////////////////////////////////////////////////////////////////// 187 188 public boolean isArray(final String path) { 189 return isArray(getNode(path)); 190 } 191 192 public boolean isArray() { 193 return isArray(asJsonNode()); 194 } 195 196 private boolean isArray(final JsonNode node) { 197 return !representsNull(node) && node.isArray(); 198 } 199 200 public JsonRepresentation getArray(final String path) { 201 return getArray(path, getNode(path)); 202 } 203 204 public JsonRepresentation asArray() { 205 return getArray(null, asJsonNode()); 206 } 207 208 private JsonRepresentation getArray(final String path, final JsonNode node) { 209 if (representsNull(node)) { 210 return null; 211 } 212 213 if (!isArray(node)) { 214 throw new IllegalArgumentException(formatExMsg(path, "is not an array")); 215 } 216 return new JsonRepresentation(node); 217 } 218 219 public JsonRepresentation getArrayEnsured(final String path) { 220 return getArrayEnsured(path, getNode(path)); 221 } 222 223 private JsonRepresentation getArrayEnsured(final String path, final JsonNode node) { 224 if (representsNull(node)) { 225 return null; 226 } 227 return new JsonRepresentation(node).ensureArray(); 228 } 229 230 // /////////////////////////////////////////////////////////////////////// 231 // isMap, getMap, asMap 232 // /////////////////////////////////////////////////////////////////////// 233 234 public boolean isMap(final String path) { 235 return isMap(getNode(path)); 236 } 237 238 public boolean isMap() { 239 return isMap(asJsonNode()); 240 } 241 242 private boolean isMap(final JsonNode node) { 243 return !representsNull(node) && !node.isArray() && !node.isValueNode(); 244 } 245 246 public JsonRepresentation getMap(final String path) { 247 return getMap(path, getNode(path)); 248 } 249 250 public JsonRepresentation asMap() { 251 return getMap(null, asJsonNode()); 252 } 253 254 private JsonRepresentation getMap(final String path, final JsonNode node) { 255 if (representsNull(node)) { 256 return null; 257 } 258 if (isArray(node) || node.isValueNode()) { 259 throw new IllegalArgumentException(formatExMsg(path, "is not a map")); 260 } 261 return new JsonRepresentation(node); 262 } 263 264 // /////////////////////////////////////////////////////////////////////// 265 // isNumber 266 // /////////////////////////////////////////////////////////////////////// 267 268 public boolean isNumber(final String path) { 269 return isNumber(getNode(path)); 270 } 271 272 public boolean isNumber() { 273 return isNumber(asJsonNode()); 274 } 275 276 private boolean isNumber(final JsonNode node) { 277 return !representsNull(node) && node.isValueNode() && node.isNumber(); 278 } 279 280 public Number asNumber() { 281 return getNumber(null, asJsonNode()); 282 } 283 284 private Number getNumber(final String path, final JsonNode node) { 285 if (representsNull(node)) { 286 return null; 287 } 288 checkValue(path, node, "a number"); 289 if (!node.isNumber()) { 290 throw new IllegalArgumentException(formatExMsg(path, "is not a number")); 291 } 292 return node.getNumberValue(); 293 } 294 295 296 // /////////////////////////////////////////////////////////////////////// 297 // isIntegralNumber, getIntegralNumber, asIntegralNumber 298 // /////////////////////////////////////////////////////////////////////// 299 300 /** 301 * Is a long, an int or a {@link BigInteger}. 302 */ 303 public boolean isIntegralNumber(final String path) { 304 return isIntegralNumber(getNode(path)); 305 } 306 307 /** 308 * Is a long, an int or a {@link BigInteger}. 309 */ 310 public boolean isIntegralNumber() { 311 return isIntegralNumber(asJsonNode()); 312 } 313 314 private boolean isIntegralNumber(final JsonNode node) { 315 return !representsNull(node) && node.isValueNode() && node.isIntegralNumber(); 316 } 317 318 319 // /////////////////////////////////////////////////////////////////////// 320 // getDate, asDate 321 // /////////////////////////////////////////////////////////////////////// 322 323 public final static DateTimeFormatter yyyyMMdd = DateTimeFormat.forPattern("yyyy-MM-dd"); 324 325 public java.util.Date getDate(final String path) { 326 return getDate(path, getNode(path)); 327 } 328 329 public java.util.Date asDate() { 330 return getDate(null, asJsonNode()); 331 } 332 333 private java.util.Date getDate(final String path, final JsonNode node) { 334 if (representsNull(node)) { 335 return null; 336 } 337 checkValue(path, node, "a date"); 338 if (!node.isTextual()) { 339 throw new IllegalArgumentException(formatExMsg(path, "is not a date")); 340 } 341 final String textValue = node.getTextValue(); 342 return new java.util.Date(yyyyMMdd.parseMillis(textValue)); 343 } 344 345 // /////////////////////////////////////////////////////////////////////// 346 // getDateTime, asDateTime 347 // /////////////////////////////////////////////////////////////////////// 348 349 public final static DateTimeFormatter yyyyMMddTHHmmssZ = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ"); 350 351 public java.util.Date getDateTime(final String path) { 352 return getDateTime(path, getNode(path)); 353 } 354 355 public java.util.Date asDateTime() { 356 return getDateTime(null, asJsonNode()); 357 } 358 359 private java.util.Date getDateTime(final String path, final JsonNode node) { 360 if (representsNull(node)) { 361 return null; 362 } 363 checkValue(path, node, "a date-time"); 364 if (!node.isTextual()) { 365 throw new IllegalArgumentException(formatExMsg(path, "is not a date-time")); 366 } 367 final String textValue = node.getTextValue(); 368 return new java.util.Date(yyyyMMddTHHmmssZ.parseMillis(textValue)); 369 } 370 371 // /////////////////////////////////////////////////////////////////////// 372 // getTime, asTime 373 // /////////////////////////////////////////////////////////////////////// 374 375 public final static DateTimeFormatter _HHmmss = DateTimeFormat.forPattern("HH:mm:ss"); 376 377 public java.util.Date getTime(final String path) { 378 return getTime(path, getNode(path)); 379 } 380 381 public java.util.Date asTime() { 382 return getTime(null, asJsonNode()); 383 } 384 385 private java.util.Date getTime(final String path, final JsonNode node) { 386 if (representsNull(node)) { 387 return null; 388 } 389 checkValue(path, node, "a time"); 390 if (!node.isTextual()) { 391 throw new IllegalArgumentException(formatExMsg(path, "is not a time")); 392 } 393 final String textValue = node.getTextValue(); 394 return new java.util.Date(_HHmmss.parseMillis(textValue)); 395 } 396 397 // /////////////////////////////////////////////////////////////////////// 398 // isBoolean, getBoolean, asBoolean 399 // /////////////////////////////////////////////////////////////////////// 400 401 public boolean isBoolean(final String path) { 402 return isBoolean(getNode(path)); 403 } 404 405 public boolean isBoolean() { 406 return isBoolean(asJsonNode()); 407 } 408 409 private boolean isBoolean(final JsonNode node) { 410 return !representsNull(node) && node.isValueNode() && node.isBoolean(); 411 } 412 413 /** 414 * Use {@link #isBoolean(String)} to check first, if required. 415 */ 416 public Boolean getBoolean(final String path) { 417 return getBoolean(path, getNode(path)); 418 } 419 420 /** 421 * Use {@link #isBoolean()} to check first, if required. 422 */ 423 public Boolean asBoolean() { 424 return getBoolean(null, asJsonNode()); 425 } 426 427 private Boolean getBoolean(final String path, final JsonNode node) { 428 if (representsNull(node)) { 429 return null; 430 } 431 checkValue(path, node, "a boolean"); 432 if (!node.isBoolean()) { 433 throw new IllegalArgumentException(formatExMsg(path, "is not a boolean")); 434 } 435 return node.getBooleanValue(); 436 } 437 438 // /////////////////////////////////////////////////////////////////////// 439 // getByte, asByte 440 // /////////////////////////////////////////////////////////////////////// 441 442 /** 443 * Use {@link #isIntegralNumber(String)} to test if number (it is not possible to check if a byte, however). 444 */ 445 public Byte getByte(final String path) { 446 final JsonNode node = getNode(path); 447 return getByte(path, node); 448 } 449 450 /** 451 * Use {@link #isIntegralNumber()} to test if number (it is not possible to check if a byte, however). 452 */ 453 public Byte asByte() { 454 return getByte(null, asJsonNode()); 455 } 456 457 private Byte getByte(final String path, final JsonNode node) { 458 if (representsNull(node)) { 459 return null; 460 } 461 checkValue(path, node, "an byte"); 462 if (!node.isNumber()) { 463 // there is no node.isByte() 464 throw new IllegalArgumentException(formatExMsg(path, "is not a number")); 465 } 466 return node.getNumberValue().byteValue(); 467 } 468 469 // /////////////////////////////////////////////////////////////////////// 470 // getShort, asShort 471 // /////////////////////////////////////////////////////////////////////// 472 473 /** 474 * Use {@link #isIntegralNumber(String)} to check if number (it is not possible to check if a short, however). 475 */ 476 public Short getShort(final String path) { 477 final JsonNode node = getNode(path); 478 return getShort(path, node); 479 } 480 481 /** 482 * Use {@link #isIntegralNumber()} to check if number (it is not possible to check if a short, however). 483 */ 484 public Short asShort() { 485 return getShort(null, asJsonNode()); 486 } 487 488 private Short getShort(final String path, final JsonNode node) { 489 if (representsNull(node)) { 490 return null; 491 } 492 checkValue(path, node, "an short"); 493 if (!node.isNumber()) { 494 // there is no node.isShort() 495 throw new IllegalArgumentException(formatExMsg(path, "is not a number")); 496 } 497 return node.getNumberValue().shortValue(); 498 } 499 500 501 // /////////////////////////////////////////////////////////////////////// 502 // getChar, asChar 503 // /////////////////////////////////////////////////////////////////////// 504 505 /** 506 * Use {@link #isString(String)} to check if string (it is not possible to check if a character, however). 507 */ 508 public Character getChar(final String path) { 509 final JsonNode node = getNode(path); 510 return getChar(path, node); 511 } 512 513 /** 514 * Use {@link #isString()} to check if string (it is not possible to check if a character, however). 515 */ 516 public Character asChar() { 517 return getChar(null, asJsonNode()); 518 } 519 520 private Character getChar(final String path, final JsonNode node) { 521 if (representsNull(node)) { 522 return null; 523 } 524 checkValue(path, node, "an short"); 525 if (!node.isTextual()) { 526 throw new IllegalArgumentException(formatExMsg(path, "is not textual")); 527 } 528 final String textValue = node.getTextValue(); 529 if(textValue == null || textValue.length() == 0) { 530 return null; 531 } 532 return textValue.charAt(0); 533 } 534 535 536 // /////////////////////////////////////////////////////////////////////// 537 // isInt, getInt, asInt 538 // /////////////////////////////////////////////////////////////////////// 539 540 public boolean isInt(final String path) { 541 return isInt(getNode(path)); 542 } 543 544 public boolean isInt() { 545 return isInt(asJsonNode()); 546 } 547 548 private boolean isInt(final JsonNode node) { 549 return !representsNull(node) && node.isValueNode() && node.isInt(); 550 } 551 552 /** 553 * Use {@link #isInt(String)} to check first, if required. 554 */ 555 public Integer getInt(final String path) { 556 final JsonNode node = getNode(path); 557 return getInt(path, node); 558 } 559 560 /** 561 * Use {@link #isInt()} to check first, if required. 562 */ 563 public Integer asInt() { 564 return getInt(null, asJsonNode()); 565 } 566 567 private Integer getInt(final String path, final JsonNode node) { 568 if (representsNull(node)) { 569 return null; 570 } 571 checkValue(path, node, "an int"); 572 if (!node.isInt()) { 573 throw new IllegalArgumentException(formatExMsg(path, "is not an int")); 574 } 575 return node.getIntValue(); 576 } 577 578 579 // /////////////////////////////////////////////////////////////////////// 580 // isLong, getLong, asLong 581 // /////////////////////////////////////////////////////////////////////// 582 583 public boolean isLong(final String path) { 584 return isLong(getNode(path)); 585 } 586 587 public boolean isLong() { 588 return isLong(asJsonNode()); 589 } 590 591 private boolean isLong(final JsonNode node) { 592 return !representsNull(node) && node.isValueNode() && node.isLong(); 593 } 594 595 /** 596 * Use {@link #isLong(String)} to check first, if required. 597 */ 598 public Long getLong(final String path) { 599 final JsonNode node = getNode(path); 600 return getLong(path, node); 601 } 602 603 /** 604 * Use {@link #isLong()} to check first, if required. 605 */ 606 public Long asLong() { 607 return getLong(null, asJsonNode()); 608 } 609 610 private Long getLong(final String path, final JsonNode node) { 611 if (representsNull(node)) { 612 return null; 613 } 614 checkValue(path, node, "a long"); 615 if (!node.isLong()) { 616 throw new IllegalArgumentException(formatExMsg(path, "is not a long")); 617 } 618 return node.getLongValue(); 619 } 620 621 // /////////////////////////////////////////////////////////////////////// 622 // getFloat, asFloat 623 // /////////////////////////////////////////////////////////////////////// 624 625 /** 626 * Use {@link #isNumber(String)} to test if number (it is not possible to check if a float, however). 627 */ 628 public Float getFloat(final String path) { 629 final JsonNode node = getNode(path); 630 return getFloat(path, node); 631 } 632 633 /** 634 * Use {@link #isNumber()} to test if number (it is not possible to check if a float, however). 635 */ 636 public Float asFloat() { 637 return getFloat(null, asJsonNode()); 638 } 639 640 private Float getFloat(final String path, final JsonNode node) { 641 if (representsNull(node)) { 642 return null; 643 } 644 checkValue(path, node, "a float"); 645 if (!node.isNumber()) { 646 throw new IllegalArgumentException(formatExMsg(path, "is not a number")); 647 } 648 return node.getNumberValue().floatValue(); 649 } 650 651 652 // /////////////////////////////////////////////////////////////////////// 653 // isDouble, getDouble, asDouble 654 // /////////////////////////////////////////////////////////////////////// 655 656 public boolean isDouble(final String path) { 657 return isDouble(getNode(path)); 658 } 659 660 public boolean isDouble() { 661 return isDouble(asJsonNode()); 662 } 663 664 private boolean isDouble(final JsonNode node) { 665 return !representsNull(node) && node.isValueNode() && node.isDouble(); 666 } 667 668 /** 669 * Use {@link #isDouble(String)} to check first, if required. 670 */ 671 public Double getDouble(final String path) { 672 final JsonNode node = getNode(path); 673 return getDouble(path, node); 674 } 675 676 /** 677 * Use {@link #isDouble()} to check first, if required. 678 */ 679 public Double asDouble() { 680 return getDouble(null, asJsonNode()); 681 } 682 683 private Double getDouble(final String path, final JsonNode node) { 684 if (representsNull(node)) { 685 return null; 686 } 687 checkValue(path, node, "a double"); 688 if (!node.isDouble()) { 689 throw new IllegalArgumentException(formatExMsg(path, "is not a double")); 690 } 691 return node.getDoubleValue(); 692 } 693 694 // /////////////////////////////////////////////////////////////////////// 695 // isBigInteger, getBigInteger, asBigInteger 696 // /////////////////////////////////////////////////////////////////////// 697 698 public boolean isBigInteger(final String path) { 699 return isBigInteger(getNode(path)); 700 } 701 702 public boolean isBigInteger() { 703 return isBigInteger(asJsonNode()); 704 } 705 706 private boolean isBigInteger(final JsonNode node) { 707 return !representsNull(node) && node.isValueNode() && node.isBigInteger(); 708 } 709 710 /** 711 * Use {@link #isBigInteger(String)} to check first, if required. 712 */ 713 public BigInteger getBigInteger(final String path) { 714 final JsonNode node = getNode(path); 715 return getBigInteger(path, node); 716 } 717 718 /** 719 * Use {@link #isBigInteger()} to check first, if required. 720 */ 721 public BigInteger asBigInteger() { 722 return getBigInteger(null, asJsonNode()); 723 } 724 725 private BigInteger getBigInteger(final String path, final JsonNode node) { 726 if (representsNull(node)) { 727 return null; 728 } 729 checkValue(path, node, "a biginteger"); 730 if (!node.isBigInteger()) { 731 throw new IllegalArgumentException(formatExMsg(path, "is not a biginteger")); 732 } 733 return node.getBigIntegerValue(); 734 } 735 736 // /////////////////////////////////////////////////////////////////////// 737 // isBigDecimal, getBigDecimal, asBigDecimal 738 // /////////////////////////////////////////////////////////////////////// 739 740 public boolean isBigDecimal(final String path) { 741 return isBigDecimal(getNode(path)); 742 } 743 public boolean isBigDecimalOrNumeric(final String path) { 744 return isBigDecimalOrNumeric(getNode(path)); 745 } 746 747 public boolean isBigDecimal() { 748 return isBigDecimal(asJsonNode()); 749 } 750 public boolean isBigDecimalOrNumeric() { 751 return isBigDecimalOrNumeric(asJsonNode()); 752 } 753 754 private boolean isBigDecimal(final JsonNode node) { 755 return !representsNull(node) && node.isValueNode() && node.isBigDecimal(); 756 } 757 private boolean isBigDecimalOrNumeric(final JsonNode node) { 758 return !representsNull(node) && node.isValueNode() && (node.isBigDecimal() || node.isDouble() || node.isLong() || node.isInt() || node.isBigInteger()); 759 } 760 761 /** 762 * Use {@link #isBigDecimal(String)} to check first, if required. 763 */ 764 public BigDecimal getBigDecimal(final String path) { 765 final JsonNode node = getNode(path); 766 return getBigDecimal(path, node); 767 } 768 /** 769 * Use {@link #isBigDecimal(String)} to check first, if required. 770 */ 771 public BigDecimal getBigDecimalFromNumeric(final String path) { 772 final JsonNode node = getNode(path); 773 return getBigDecimalFromNumeric(path, node); 774 } 775 776 /** 777 * Use {@link #isBigDecimal()} to check first, if required. 778 */ 779 public BigDecimal asBigDecimal() { 780 return getBigDecimal(null, asJsonNode()); 781 } 782 783 /** 784 * Use {@link #isBigDecimalOrNumeric()} to check first, if required. 785 */ 786 public BigDecimal asBigDecimalFromNumeric() { 787 return getBigDecimalFromNumeric(null, asJsonNode()); 788 } 789 790 private BigDecimal getBigDecimal(final String path, final JsonNode node) { 791 if (representsNull(node)) { 792 return null; 793 } 794 checkValue(path, node, "a bigdecimal"); 795 if (node.isBigDecimal()) { 796 throw new IllegalArgumentException(formatExMsg(path, "is not a bigdecimal (or any other numeric)")); 797 } 798 return node.getDecimalValue(); 799 } 800 801 private BigDecimal getBigDecimalFromNumeric(final String path, final JsonNode node) { 802 if (representsNull(node)) { 803 return null; 804 } 805 checkValue(path, node, "a bigdecimal"); 806 if (node.isBigDecimal()) { 807 return node.getDecimalValue(); 808 } 809 if (node.isLong()) { 810 // there will be rounding errors, most likely 811 return new BigDecimal(node.getLongValue()); 812 } 813 if (node.isDouble()) { 814 // there will be rounding errors, most likely 815 return new BigDecimal(node.getDoubleValue()); 816 } 817 if (node.isBigInteger()) { 818 // there will be rounding errors, most likely 819 return new BigDecimal(node.getBigIntegerValue()); 820 } 821 if (node.isInt()) { 822 // there will be rounding errors, most likely 823 return new BigDecimal(node.getIntValue()); 824 } 825 throw new IllegalArgumentException(formatExMsg(path, "is not a bigdecimal (or any other numeric)")); 826 } 827 828 829 // /////////////////////////////////////////////////////////////////////// 830 // getString, isString, asString 831 // /////////////////////////////////////////////////////////////////////// 832 833 public boolean isString(final String path) { 834 return isString(getNode(path)); 835 } 836 837 public boolean isString() { 838 return isString(asJsonNode()); 839 } 840 841 private boolean isString(final JsonNode node) { 842 return !representsNull(node) && node.isValueNode() && node.isTextual(); 843 } 844 845 /** 846 * Use {@link #isString(String)} to check first, if required. 847 */ 848 public String getString(final String path) { 849 final JsonNode node = getNode(path); 850 return getString(path, node); 851 } 852 853 /** 854 * Use {@link #isString()} to check first, if required. 855 */ 856 public String asString() { 857 return getString(null, asJsonNode()); 858 } 859 860 private String getString(final String path, final JsonNode node) { 861 if (representsNull(node)) { 862 return null; 863 } 864 checkValue(path, node, "a string"); 865 if (!node.isTextual()) { 866 throw new IllegalArgumentException(formatExMsg(path, "is not a string")); 867 } 868 return node.getTextValue(); 869 } 870 871 public String asArg() { 872 if (isValue()) { 873 return asJsonNode().getValueAsText(); 874 } else { 875 return asJsonNode().toString(); 876 } 877 } 878 879 // /////////////////////////////////////////////////////////////////////// 880 // isLink, getLink, asLink 881 // /////////////////////////////////////////////////////////////////////// 882 883 public boolean isLink() { 884 return isLink(asJsonNode()); 885 } 886 887 public boolean isLink(final String path) { 888 return isLink(getNode(path)); 889 } 890 891 public boolean isLink(final JsonNode node) { 892 if (representsNull(node) || isArray(node) || node.isValueNode()) { 893 return false; 894 } 895 896 final LinkRepresentation link = new LinkRepresentation(node); 897 if (link.getHref() == null) { 898 return false; 899 } 900 return true; 901 } 902 903 /** 904 * Use {@link #isLink(String)} to check first, if required. 905 */ 906 public LinkRepresentation getLink(final String path) { 907 return getLink(path, getNode(path)); 908 } 909 910 /** 911 * Use {@link #isLink()} to check first, if required. 912 */ 913 public LinkRepresentation asLink() { 914 return getLink(null, asJsonNode()); 915 } 916 917 private LinkRepresentation getLink(final String path, final JsonNode node) { 918 if (representsNull(node)) { 919 return null; 920 } 921 922 if (isArray(node)) { 923 throw new IllegalArgumentException(formatExMsg(path, "is an array that does not represent a link")); 924 } 925 if (node.isValueNode()) { 926 throw new IllegalArgumentException(formatExMsg(path, "is a value that does not represent a link")); 927 } 928 929 final LinkRepresentation link = new LinkRepresentation(node); 930 if (link.getHref() == null) { 931 throw new IllegalArgumentException(formatExMsg(path, "is a map that does not fully represent a link")); 932 } 933 return link; 934 } 935 936 // /////////////////////////////////////////////////////////////////////// 937 // getNull, isNull 938 // /////////////////////////////////////////////////////////////////////// 939 940 public boolean isNull() { 941 return isNull(asJsonNode()); 942 } 943 944 /** 945 * Indicates that the wrapped node has <tt>null</tt> value (ie 946 * {@link JsonRepresentation#isNull()}), or returns <tt>null</tt> if there 947 * was no node with the provided path. 948 */ 949 public Boolean isNull(final String path) { 950 return isNull(getNode(path)); 951 } 952 953 private Boolean isNull(final JsonNode node) { 954 if (node == null || node.isMissingNode()) { 955 // not exclude if node.isNull, cos that's the point of this. 956 return null; 957 } 958 return node.isNull(); 959 } 960 961 /** 962 * Either returns a {@link JsonRepresentation} that indicates that the 963 * wrapped node has <tt>null</tt> value (ie 964 * {@link JsonRepresentation#isNull()}), or returns <tt>null</tt> if there 965 * was no node with the provided path. 966 * 967 * <p> 968 * Use {@link #isNull(String)} to check first, if required. 969 */ 970 public JsonRepresentation getNull(final String path) { 971 return getNull(path, getNode(path)); 972 } 973 974 /** 975 * Either returns a {@link JsonRepresentation} that indicates that the 976 * wrapped node has <tt>null</tt> value (ie 977 * {@link JsonRepresentation#isNull()}), or returns <tt>null</tt> if there 978 * was no node with the provided path. 979 * 980 * <p> 981 * Use {@link #isNull()} to check first, if required. 982 */ 983 public JsonRepresentation asNull() { 984 return getNull(null, asJsonNode()); 985 } 986 987 private JsonRepresentation getNull(final String path, final JsonNode node) { 988 if (node == null || node.isMissingNode()) { 989 // exclude if node.isNull, cos that's the point of this. 990 return null; 991 } 992 checkValue(path, node, "the null value"); 993 if (!node.isNull()) { 994 throw new IllegalArgumentException(formatExMsg(path, "is not the null value")); 995 } 996 return new JsonRepresentation(node); 997 } 998 999 // /////////////////////////////////////////////////////////////////////// 1000 // mapValueAsLink 1001 // /////////////////////////////////////////////////////////////////////// 1002 1003 /** 1004 * Convert a representation that contains a single node representing a link 1005 * into a {@link LinkRepresentation}. 1006 */ 1007 public LinkRepresentation mapValueAsLink() { 1008 if (asJsonNode().size() != 1) { 1009 throw new IllegalStateException("does not represent link"); 1010 } 1011 final String linkPropertyName = asJsonNode().getFieldNames().next(); 1012 return getLink(linkPropertyName); 1013 } 1014 1015 // /////////////////////////////////////////////////////////////////////// 1016 // asInputStream 1017 // /////////////////////////////////////////////////////////////////////// 1018 1019 public InputStream asInputStream() { 1020 return JsonNodeUtils.asInputStream(jsonNode); 1021 } 1022 1023 // /////////////////////////////////////////////////////////////////////// 1024 // asArrayNode, asObjectNode 1025 // /////////////////////////////////////////////////////////////////////// 1026 1027 /** 1028 * Convert underlying representation into an array. 1029 */ 1030 protected ArrayNode asArrayNode() { 1031 if (!isArray()) { 1032 throw new IllegalStateException("does not represent array"); 1033 } 1034 return (ArrayNode) asJsonNode(); 1035 } 1036 1037 /** 1038 * Convert underlying representation into an object (map). 1039 */ 1040 protected ObjectNode asObjectNode() { 1041 if (!isMap()) { 1042 throw new IllegalStateException("does not represent map"); 1043 } 1044 return (ObjectNode) asJsonNode(); 1045 } 1046 1047 // /////////////////////////////////////////////////////////////////////// 1048 // asT 1049 // /////////////////////////////////////////////////////////////////////// 1050 1051 /** 1052 * Convenience to simply "downcast". 1053 * 1054 * <p> 1055 * In fact, the method creates a new instance of the specified type, which 1056 * shares the underlying {@link #jsonNode jsonNode}. 1057 */ 1058 public <T extends JsonRepresentation> T as(final Class<T> cls) { 1059 try { 1060 final Constructor<T> constructor = cls.getConstructor(JsonNode.class); 1061 return constructor.newInstance(jsonNode); 1062 } catch (final Exception e) { 1063 throw new RuntimeException(e); 1064 } 1065 } 1066 1067 // /////////////////////////////////////////////////////////////////////// 1068 // asUrlEncoded 1069 // /////////////////////////////////////////////////////////////////////// 1070 1071 public String asUrlEncoded() { 1072 return UrlEncodingUtils.urlEncode(asJsonNode()); 1073 } 1074 1075 // /////////////////////////////////////////////////////////////////////// 1076 // mutable (array) 1077 // /////////////////////////////////////////////////////////////////////// 1078 1079 public JsonRepresentation arrayAdd(final Object value) { 1080 if (!isArray()) { 1081 throw new IllegalStateException("does not represent array"); 1082 } 1083 asArrayNode().add(new POJONode(value)); 1084 return this; 1085 } 1086 1087 public JsonRepresentation arrayAdd(final JsonRepresentation value) { 1088 if (!isArray()) { 1089 throw new IllegalStateException("does not represent array"); 1090 } 1091 asArrayNode().add(value.asJsonNode()); 1092 return this; 1093 } 1094 1095 public JsonRepresentation arrayAdd(final String value) { 1096 if (!isArray()) { 1097 throw new IllegalStateException("does not represent array"); 1098 } 1099 asArrayNode().add(value); 1100 return this; 1101 } 1102 1103 public JsonRepresentation arrayAdd(final JsonNode value) { 1104 if (!isArray()) { 1105 throw new IllegalStateException("does not represent array"); 1106 } 1107 asArrayNode().add(value); 1108 return this; 1109 } 1110 1111 public JsonRepresentation arrayAdd(final long value) { 1112 if (!isArray()) { 1113 throw new IllegalStateException("does not represent array"); 1114 } 1115 asArrayNode().add(value); 1116 return this; 1117 } 1118 1119 public JsonRepresentation arrayAdd(final int value) { 1120 if (!isArray()) { 1121 throw new IllegalStateException("does not represent array"); 1122 } 1123 asArrayNode().add(value); 1124 return this; 1125 } 1126 1127 public JsonRepresentation arrayAdd(final double value) { 1128 if (!isArray()) { 1129 throw new IllegalStateException("does not represent array"); 1130 } 1131 asArrayNode().add(value); 1132 return this; 1133 } 1134 1135 public JsonRepresentation arrayAdd(final float value) { 1136 if (!isArray()) { 1137 throw new IllegalStateException("does not represent array"); 1138 } 1139 asArrayNode().add(value); 1140 return this; 1141 } 1142 1143 public JsonRepresentation arrayAdd(final boolean value) { 1144 if (!isArray()) { 1145 throw new IllegalStateException("does not represent array"); 1146 } 1147 asArrayNode().add(value); 1148 return this; 1149 } 1150 1151 public Iterable<JsonRepresentation> arrayIterable() { 1152 return arrayIterable(JsonRepresentation.class); 1153 } 1154 1155 public <T> Iterable<T> arrayIterable(final Class<T> requiredType) { 1156 return new Iterable<T>() { 1157 @Override 1158 public Iterator<T> iterator() { 1159 return arrayIterator(requiredType); 1160 } 1161 }; 1162 } 1163 1164 public Iterator<JsonRepresentation> arrayIterator() { 1165 return arrayIterator(JsonRepresentation.class); 1166 } 1167 1168 public <T> Iterator<T> arrayIterator(final Class<T> requiredType) { 1169 ensureIsAnArrayAtLeastAsLargeAs(0); 1170 final Function<JsonNode, ?> transformer = representationInstantiatorFor(requiredType); 1171 final ArrayNode arrayNode = (ArrayNode) jsonNode; 1172 final Iterator<JsonNode> iterator = arrayNode.iterator(); 1173 // necessary to do in two steps 1174 final Function<JsonNode, T> typedTransformer = asT(transformer); 1175 return Iterators.transform(iterator, typedTransformer); 1176 } 1177 1178 @SuppressWarnings("unchecked") 1179 private static <T> Function<JsonNode, T> asT(final Function<JsonNode, ?> transformer) { 1180 return (Function<JsonNode, T>) transformer; 1181 } 1182 1183 public JsonRepresentation arrayGet(final int i) { 1184 ensureIsAnArrayAtLeastAsLargeAs(i+1); 1185 return new JsonRepresentation(jsonNode.get(i)); 1186 } 1187 1188 public JsonRepresentation arraySetElementAt(final int i, final JsonRepresentation objectRepr) { 1189 ensureIsAnArrayAtLeastAsLargeAs(i+1); 1190 if (objectRepr.isArray()) { 1191 throw new IllegalArgumentException("Representation being set cannot be an array"); 1192 } 1193 // can safely downcast because *this* representation is an array 1194 final ArrayNode arrayNode = (ArrayNode) jsonNode; 1195 arrayNode.set(i, objectRepr.asJsonNode()); 1196 return this; 1197 } 1198 1199 private void ensureIsAnArrayAtLeastAsLargeAs(final int i) { 1200 if (!jsonNode.isArray()) { 1201 throw new IllegalStateException("Is not an array"); 1202 } 1203 if (i > size()) { 1204 throw new IndexOutOfBoundsException("array has only " + size() + " elements"); 1205 } 1206 } 1207 1208 // /////////////////////////////////////////////////////////////////////// 1209 // mutable (map) 1210 // /////////////////////////////////////////////////////////////////////// 1211 1212 public boolean mapHas(final String key) { 1213 if (!isMap()) { 1214 throw new IllegalStateException("does not represent map"); 1215 } 1216 ObjectNode node = asObjectNode(); 1217 1218 final String[] paths = key.split("\\."); 1219 for (int i = 0; i < paths.length; i++) { 1220 final String path = paths[i]; 1221 final boolean has = node.has(path); 1222 if (!has) { 1223 return false; 1224 } 1225 if (i + 1 < paths.length) { 1226 // not on last 1227 final JsonNode subNode = node.get(path); 1228 if (!subNode.isObject()) { 1229 return false; 1230 } 1231 node = (ObjectNode) subNode; 1232 } 1233 } 1234 return true; 1235 } 1236 1237 public JsonRepresentation mapPut(final String key, final List<Object> value) { 1238 if (!isMap()) { 1239 throw new IllegalStateException("does not represent map"); 1240 } 1241 if (value == null) { 1242 return this; 1243 } 1244 final JsonRepresentation array = JsonRepresentation.newArray(); 1245 for (final Object v : value) { 1246 array.arrayAdd(v); 1247 } 1248 mapPut(key, array); 1249 return this; 1250 } 1251 1252 public JsonRepresentation mapPut(final String key, final Object value) { 1253 if (!isMap()) { 1254 throw new IllegalStateException("does not represent map"); 1255 } 1256 final Path path = Path.parse(key); 1257 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1258 node.put(path.getTail(), value != null? new POJONode(value): NullNode.getInstance() ); 1259 return this; 1260 } 1261 1262 public JsonRepresentation mapPut(final String key, final JsonRepresentation value) { 1263 if (!isMap()) { 1264 throw new IllegalStateException("does not represent map"); 1265 } 1266 if (value == null) { 1267 return this; 1268 } 1269 final Path path = Path.parse(key); 1270 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1271 node.put(path.getTail(), value.asJsonNode()); 1272 return this; 1273 } 1274 1275 public JsonRepresentation mapPut(final String key, final String value) { 1276 if (!isMap()) { 1277 throw new IllegalStateException("does not represent map"); 1278 } 1279 if (value == null) { 1280 return this; 1281 } 1282 final Path path = Path.parse(key); 1283 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1284 node.put(path.getTail(), value); 1285 return this; 1286 } 1287 1288 public JsonRepresentation mapPut(final String key, final JsonNode value) { 1289 if (!isMap()) { 1290 throw new IllegalStateException("does not represent map"); 1291 } 1292 if (value == null) { 1293 return this; 1294 } 1295 final Path path = Path.parse(key); 1296 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1297 node.put(path.getTail(), value); 1298 return this; 1299 } 1300 1301 public JsonRepresentation mapPut(final String key, final long value) { 1302 if (!isMap()) { 1303 throw new IllegalStateException("does not represent map"); 1304 } 1305 final Path path = Path.parse(key); 1306 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1307 node.put(path.getTail(), value); 1308 return this; 1309 } 1310 1311 public JsonRepresentation mapPut(final String key, final int value) { 1312 if (!isMap()) { 1313 throw new IllegalStateException("does not represent map"); 1314 } 1315 final Path path = Path.parse(key); 1316 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1317 node.put(path.getTail(), value); 1318 return this; 1319 } 1320 1321 public JsonRepresentation mapPut(final String key, final double value) { 1322 if (!isMap()) { 1323 throw new IllegalStateException("does not represent map"); 1324 } 1325 final Path path = Path.parse(key); 1326 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1327 node.put(path.getTail(), value); 1328 return this; 1329 } 1330 1331 public JsonRepresentation mapPut(final String key, final float value) { 1332 if (!isMap()) { 1333 throw new IllegalStateException("does not represent map"); 1334 } 1335 final Path path = Path.parse(key); 1336 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1337 node.put(path.getTail(), value); 1338 return this; 1339 } 1340 1341 public JsonRepresentation mapPut(final String key, final boolean value) { 1342 if (!isMap()) { 1343 throw new IllegalStateException("does not represent map"); 1344 } 1345 final Path path = Path.parse(key); 1346 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1347 node.put(path.getTail(), value); 1348 return this; 1349 } 1350 1351 public JsonRepresentation mapPut(final String key, final BigInteger value) { 1352 if (!isMap()) { 1353 throw new IllegalStateException("does not represent map"); 1354 } 1355 final Path path = Path.parse(key); 1356 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1357 node.put(path.getTail(), value != null? new BigIntegerNode(value): NullNode.getInstance() ); 1358 return this; 1359 1360 } 1361 1362 public JsonRepresentation mapPut(final String key, final BigDecimal value) { 1363 if (!isMap()) { 1364 throw new IllegalStateException("does not represent map"); 1365 } 1366 final Path path = Path.parse(key); 1367 final ObjectNode node = JsonNodeUtils.walkNodeUpTo(asObjectNode(), path.getHead()); 1368 node.put(path.getTail(), value != null? new DecimalNode(value): NullNode.getInstance() ); 1369 return this; 1370 } 1371 1372 private static class Path { 1373 private final List<String> head; 1374 private final String tail; 1375 1376 private Path(final List<String> head, final String tail) { 1377 this.head = Collections.unmodifiableList(head); 1378 this.tail = tail; 1379 } 1380 1381 public List<String> getHead() { 1382 return head; 1383 } 1384 1385 public String getTail() { 1386 return tail; 1387 } 1388 1389 public static Path parse(final String pathStr) { 1390 final List<String> keyList = Lists.newArrayList(Arrays.asList(pathStr.split("\\."))); 1391 if (keyList.size() == 0) { 1392 throw new IllegalArgumentException(String.format("Malformed path '%s'", pathStr)); 1393 } 1394 final String tail = keyList.remove(keyList.size() - 1); 1395 return new Path(keyList, tail); 1396 } 1397 } 1398 1399 public Iterable<Map.Entry<String, JsonRepresentation>> mapIterable() { 1400 ensureIsAMap(); 1401 return new Iterable<Map.Entry<String, JsonRepresentation>>() { 1402 1403 @Override 1404 public Iterator<Entry<String, JsonRepresentation>> iterator() { 1405 return mapIterator(); 1406 } 1407 }; 1408 } 1409 1410 public Iterator<Map.Entry<String, JsonRepresentation>> mapIterator() { 1411 ensureIsAMap(); 1412 return Iterators.transform(jsonNode.getFields(), MAP_ENTRY_JSON_NODE_TO_JSON_REPRESENTATION); 1413 } 1414 1415 private void ensureIsAMap() { 1416 if (!jsonNode.isObject()) { 1417 throw new IllegalStateException("Is not a map"); 1418 } 1419 } 1420 1421 private final static Function<Entry<String, JsonNode>, Entry<String, JsonRepresentation>> MAP_ENTRY_JSON_NODE_TO_JSON_REPRESENTATION = new Function<Entry<String, JsonNode>, Entry<String, JsonRepresentation>>() { 1422 1423 @Override 1424 public Entry<String, JsonRepresentation> apply(final Entry<String, JsonNode> input) { 1425 return new Map.Entry<String, JsonRepresentation>() { 1426 1427 @Override 1428 public String getKey() { 1429 return input.getKey(); 1430 } 1431 1432 @Override 1433 public JsonRepresentation getValue() { 1434 return new JsonRepresentation(input.getValue()); 1435 } 1436 1437 @Override 1438 public JsonRepresentation setValue(final JsonRepresentation value) { 1439 final JsonNode setValue = input.setValue(value.asJsonNode()); 1440 return new JsonRepresentation(setValue); 1441 } 1442 }; 1443 } 1444 }; 1445 1446 // /////////////////////////////////////////////////////////////////////// 1447 // helpers 1448 // /////////////////////////////////////////////////////////////////////// 1449 1450 /** 1451 * A reciprocal of the behaviour of the automatic dereferencing of arrays 1452 * that occurs when there is only a single instance. 1453 * 1454 * @see #toJsonNode(List) 1455 */ 1456 public JsonRepresentation ensureArray() { 1457 if (jsonNode.isArray()) { 1458 return this; 1459 } 1460 final JsonRepresentation arrayRepr = JsonRepresentation.newArray(); 1461 arrayRepr.arrayAdd(jsonNode); 1462 return arrayRepr; 1463 } 1464 1465 private JsonNode getNode(final String path) { 1466 JsonNode jsonNode = this.jsonNode; 1467 final List<String> keys = PathNode.split(path); 1468 for (final String key : keys) { 1469 final PathNode pathNode = PathNode.parse(key); 1470 if (!pathNode.getKey().isEmpty()) { 1471 jsonNode = jsonNode.path(pathNode.getKey()); 1472 } else { 1473 // pathNode is criteria only; don't change jsonNode 1474 } 1475 if (jsonNode.isNull()) { 1476 return jsonNode; 1477 } 1478 if (!pathNode.hasCriteria()) { 1479 continue; 1480 } 1481 if (!jsonNode.isArray()) { 1482 return NullNode.getInstance(); 1483 } 1484 jsonNode = matching(jsonNode, pathNode); 1485 if (jsonNode.isNull()) { 1486 return jsonNode; 1487 } 1488 } 1489 return jsonNode; 1490 } 1491 1492 private JsonNode matching(final JsonNode jsonNode, final PathNode pathNode) { 1493 final JsonRepresentation asList = new JsonRepresentation(jsonNode); 1494 final Iterable<JsonNode> filtered = Iterables.filter(asList.arrayIterable(JsonNode.class), new Predicate<JsonNode>() { 1495 @Override 1496 public boolean apply(final JsonNode input) { 1497 return pathNode.matches(new JsonRepresentation(input)); 1498 } 1499 }); 1500 final List<JsonNode> matching = Lists.newArrayList(filtered); 1501 return toJsonNode(matching); 1502 } 1503 1504 private static JsonNode toJsonNode(final List<JsonNode> matching) { 1505 switch (matching.size()) { 1506 case 0: 1507 return NullNode.getInstance(); 1508 case 1: 1509 return matching.get(0); 1510 default: 1511 final ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance); 1512 arrayNode.addAll(matching); 1513 return arrayNode; 1514 } 1515 } 1516 1517 private static void checkValue(final String path, final JsonNode node, final String requiredType) { 1518 if (node.isValueNode()) { 1519 return; 1520 } 1521 throw new IllegalArgumentException(formatExMsg(path, "is not " + requiredType)); 1522 } 1523 1524 private static boolean representsNull(final JsonNode node) { 1525 return node == null || node.isMissingNode() || node.isNull(); 1526 } 1527 1528 private static String formatExMsg(final String pathIfAny, final String errorText) { 1529 final StringBuilder buf = new StringBuilder(); 1530 if (pathIfAny != null) { 1531 buf.append("'").append(pathIfAny).append("' "); 1532 } 1533 buf.append(errorText); 1534 return buf.toString(); 1535 } 1536 1537 1538 // /////////////////////////////////////////////////////////////////////// 1539 // equals and hashcode 1540 // /////////////////////////////////////////////////////////////////////// 1541 1542 @Override 1543 public int hashCode() { 1544 final int prime = 31; 1545 int result = 1; 1546 result = prime * result + ((jsonNode == null) ? 0 : jsonNode.hashCode()); 1547 return result; 1548 } 1549 1550 @Override 1551 public boolean equals(Object obj) { 1552 if (this == obj) 1553 return true; 1554 if (obj == null) 1555 return false; 1556 if (getClass() != obj.getClass()) 1557 return false; 1558 JsonRepresentation other = (JsonRepresentation) obj; 1559 if (jsonNode == null) { 1560 if (other.jsonNode != null) 1561 return false; 1562 } else if (!jsonNode.equals(other.jsonNode)) 1563 return false; 1564 return true; 1565 } 1566 1567 // /////////////////////////////////////////////////////////////////////// 1568 // toString 1569 // /////////////////////////////////////////////////////////////////////// 1570 1571 @Override 1572 public String toString() { 1573 return jsonNode.toString(); 1574 } 1575 1576 1577 1578} 1579