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.rendering.domainobjects; 020 021import java.math.BigDecimal; 022import java.math.BigInteger; 023import java.util.Arrays; 024import java.util.Date; 025import java.util.List; 026import java.util.Map; 027 028import com.google.common.base.Function; 029import com.google.common.collect.Iterables; 030import com.google.common.collect.Lists; 031import com.google.common.collect.Maps; 032 033import org.codehaus.jackson.node.NullNode; 034import org.joda.time.DateTime; 035import org.joda.time.LocalDate; 036import org.joda.time.LocalDateTime; 037import org.joda.time.format.DateTimeFormat; 038import org.joda.time.format.DateTimeFormatter; 039import org.joda.time.format.ISODateTimeFormat; 040 041import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 042import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 043import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet; 044import org.apache.isis.core.metamodel.spec.ObjectSpecId; 045import org.apache.isis.core.metamodel.spec.ObjectSpecification; 046import org.apache.isis.core.runtime.system.context.IsisContext; 047import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 048 049/** 050 * Similar to Isis' value encoding, but with additional support for JSON 051 * primitives. 052 */ 053public final class JsonValueEncoder { 054 055 private JsonValueEncoder(){} 056 057 058 public static class ExpectedStringRepresentingValueException extends IllegalArgumentException { 059 private static final long serialVersionUID = 1L; 060 } 061 062 public static abstract class JsonValueConverter { 063 064 protected final String format; 065 protected final String xIsisFormat; 066 private final Class<?>[] classes; 067 068 public JsonValueConverter(String format, String xIsisFormat, Class<?>... classes) { 069 this.format = format; 070 this.xIsisFormat = xIsisFormat; 071 this.classes = classes; 072 } 073 074 public List<ObjectSpecId> getSpecIds() { 075 return Lists.newArrayList(Iterables.transform(Arrays.asList(classes), new Function<Class<?>, ObjectSpecId>() { 076 public ObjectSpecId apply(Class<?> cls) { 077 return new ObjectSpecId(cls.getName()); 078 } 079 })); 080 } 081 082 /** 083 * The value, otherwise <tt>null</tt>. 084 */ 085 public abstract ObjectAdapter asAdapter(JsonRepresentation repr); 086 087 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 088 append(repr, objectAdapter, format, xIsisFormat); 089 } 090 091 public Object asObject(ObjectAdapter objectAdapter) { 092 return objectAdapter.getObject(); 093 } 094 } 095 096 private static Map<ObjectSpecId, JsonValueConverter> converterBySpec = Maps.newLinkedHashMap(); 097 098 private static void putConverter(JsonValueConverter jvc) { 099 final List<ObjectSpecId> specIds = jvc.getSpecIds(); 100 for (ObjectSpecId specId : specIds) { 101 converterBySpec.put(specId, jvc); 102 } 103 } 104 105 static { 106 putConverter(new JsonValueConverter(null, "string", String.class){ 107 @Override 108 public ObjectAdapter asAdapter(JsonRepresentation repr) { 109 if (repr.isString()) { 110 return adapterFor(repr.asString()); 111 } 112 return null; 113 } 114 }); 115 116 putConverter(new JsonValueConverter(null, "boolean", boolean.class, Boolean.class){ 117 @Override 118 public ObjectAdapter asAdapter(JsonRepresentation repr) { 119 if (repr.isBoolean()) { 120 return adapterFor(repr.asBoolean()); 121 } 122 return null; 123 } 124 }); 125 126 putConverter(new JsonValueConverter(null, "byte", byte.class, Byte.class){ 127 @Override 128 public ObjectAdapter asAdapter(JsonRepresentation repr) { 129 if (repr.isNumber()) { 130 return adapterFor(repr.asNumber().byteValue()); 131 } 132 if (repr.isInt()) { 133 return adapterFor((byte)(int)repr.asInt()); 134 } 135 if (repr.isLong()) { 136 return adapterFor((byte)(long)repr.asLong()); 137 } 138 if (repr.isBigInteger()) { 139 return adapterFor(repr.asBigInteger().byteValue()); 140 } 141 return null; 142 } 143 }); 144 145 putConverter(new JsonValueConverter(null, "short", short.class, Short.class){ 146 @Override 147 public ObjectAdapter asAdapter(JsonRepresentation repr) { 148 if (repr.isNumber()) { 149 return adapterFor(repr.asNumber().shortValue()); 150 } 151 if (repr.isInt()) { 152 return adapterFor((short)(int)repr.asInt()); 153 } 154 if (repr.isLong()) { 155 return adapterFor((short)(long)repr.asLong()); 156 } 157 if (repr.isBigInteger()) { 158 return adapterFor(repr.asBigInteger().shortValue()); 159 } 160 return null; 161 } 162 }); 163 164 putConverter(new JsonValueConverter("int", "int", int.class, Integer.class){ 165 @Override 166 public ObjectAdapter asAdapter(JsonRepresentation repr) { 167 if (repr.isInt()) { 168 return adapterFor(repr.asInt()); 169 } 170 if (repr.isLong()) { 171 return adapterFor((int)(long)repr.asLong()); 172 } 173 if (repr.isBigInteger()) { 174 return adapterFor(repr.asBigInteger().intValue()); 175 } 176 if (repr.isNumber()) { 177 return adapterFor(repr.asNumber().intValue()); 178 } 179 return null; 180 } 181 }); 182 183 putConverter(new JsonValueConverter("int", "long", long.class, Long.class){ 184 @Override 185 public ObjectAdapter asAdapter(JsonRepresentation repr) { 186 if (repr.isLong()) { 187 return adapterFor(repr.asLong()); 188 } 189 if (repr.isInt()) { 190 return adapterFor(repr.asInt()); 191 } 192 if (repr.isBigInteger()) { 193 return adapterFor(repr.asBigInteger().longValue()); 194 } 195 if (repr.isNumber()) { 196 return adapterFor(repr.asNumber().longValue()); 197 } 198 return null; 199 } 200 }); 201 202 putConverter(new JsonValueConverter("decimal", "float", float.class, Float.class){ 203 @Override 204 public ObjectAdapter asAdapter(JsonRepresentation repr) { 205 if (repr.isDouble()) { 206 return adapterFor((float)(double)repr.asDouble()); 207 } 208 if (repr.isNumber()) { 209 return adapterFor(repr.asNumber().floatValue()); 210 } 211 if (repr.isLong()) { 212 return adapterFor((float)repr.asLong()); 213 } 214 if (repr.isInt()) { 215 return adapterFor((float)repr.asInt()); 216 } 217 if (repr.isBigInteger()) { 218 return adapterFor(repr.asBigInteger().floatValue()); 219 } 220 return null; 221 } 222 }); 223 224 putConverter(new JsonValueConverter("decimal", "double", double.class, Double.class){ 225 @Override 226 public ObjectAdapter asAdapter(JsonRepresentation repr) { 227 if (repr.isDouble()) { 228 return adapterFor(repr.asDouble()); 229 } 230 if (repr.isLong()) { 231 return adapterFor((double)repr.asLong()); 232 } 233 if (repr.isInt()) { 234 return adapterFor((double)repr.asInt()); 235 } 236 if (repr.isBigInteger()) { 237 return adapterFor(repr.asBigInteger().doubleValue()); 238 } 239 if (repr.isBigDecimal()) { 240 return adapterFor(repr.asBigDecimal().doubleValue()); 241 } 242 if (repr.isNumber()) { 243 return adapterFor(repr.asNumber().doubleValue()); 244 } 245 return null; 246 } 247 }); 248 249 putConverter(new JsonValueConverter(null, "char", char.class, Character.class){ 250 @Override 251 public ObjectAdapter asAdapter(JsonRepresentation repr) { 252 if (repr.isString()) { 253 final String str = repr.asString(); 254 if(str != null && str.length()>0) { 255 return adapterFor(str.charAt(0)); 256 } 257 } 258 // in case a char literal was provided 259 if(repr.isInt()) { 260 final Integer x = repr.asInt(); 261 if(Character.MIN_VALUE <= x && x <= Character.MAX_VALUE) { 262 char c = (char) x.intValue(); 263 return adapterFor(c); 264 } 265 } 266 return null; 267 } 268 }); 269 270 putConverter(new JsonValueConverter("int", "biginteger", BigInteger.class){ 271 @Override 272 public ObjectAdapter asAdapter(JsonRepresentation repr) { 273 if (repr.isBigInteger()) { 274 return adapterFor(repr.asBigInteger()); 275 } 276 if (repr.isLong()) { 277 return adapterFor(BigInteger.valueOf(repr.asLong())); 278 } 279 if (repr.isInt()) { 280 return adapterFor(BigInteger.valueOf(repr.asInt())); 281 } 282 if (repr.isNumber()) { 283 return adapterFor(BigInteger.valueOf(repr.asNumber().longValue())); 284 } 285 return null; 286 } 287 }); 288 289 putConverter(new JsonValueConverter("decimal", "bigdecimal", BigDecimal.class){ 290 @Override 291 public ObjectAdapter asAdapter(JsonRepresentation repr) { 292 // TODO: if inferring a BigDecimal, need to get the scale from somewhere... 293 if (repr.isBigDecimal()) { 294 return adapterFor(repr.asBigDecimal()); 295 } 296 if (repr.isBigInteger()) { 297 return adapterFor(new BigDecimal(repr.asBigInteger())); 298 } 299 if (repr.isDouble()) { 300 return adapterFor(BigDecimal.valueOf(repr.asDouble())); 301 } 302 if (repr.isLong()) { 303 return adapterFor(BigDecimal.valueOf(repr.asLong())); 304 } 305 if (repr.isInt()) { 306 return adapterFor(BigDecimal.valueOf(repr.asInt())); 307 } 308 return null; 309 } 310 @Override 311 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 312 super.appendValueAndFormat(objectAdapter, repr); 313 } 314 }); 315 316 putConverter(new JsonValueConverter("date", "jodalocaldate", LocalDate.class){ 317 318 final List<DateTimeFormatter> formatters = Arrays.asList( 319 JsonRepresentation.yyyyMMdd, 320 DateTimeFormat.forPattern("yyyyMMdd"), 321 ISODateTimeFormat.basicDate() 322 ); 323 324 @Override 325 public ObjectAdapter asAdapter(JsonRepresentation repr) { 326 if (repr.isString()) { 327 final String dateStr = repr.asString(); 328 for (DateTimeFormatter formatter : formatters) { 329 try { 330 final LocalDate parsedDate = formatter.parseLocalDate(dateStr); 331 return adapterFor(parsedDate); 332 } catch (IllegalArgumentException ex) { 333 // fall through 334 } 335 } 336 } 337 return null; 338 } 339 340 @Override 341 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 342 final Object obj = unwrap(objectAdapter); 343 if(obj instanceof LocalDate) { 344 final LocalDate date = (LocalDate) obj; 345 final String dateStr = formatters.get(0).print(date.toDateTimeAtStartOfDay()); 346 append(repr, dateStr, format, xIsisFormat); 347 } else { 348 append(repr, obj, format, xIsisFormat); 349 } 350 } 351 }); 352 353 putConverter(new JsonValueConverter("date-time", "jodalocaldatetime", LocalDateTime.class){ 354 355 final List<DateTimeFormatter> formatters = Arrays.asList( 356 JsonRepresentation.yyyyMMddTHHmmssZ, 357 DateTimeFormat.forPattern("yyyyMMdd'T'HHmmssZ"), 358 ISODateTimeFormat.basicDateTimeNoMillis(), 359 ISODateTimeFormat.basicDateTime() 360 ); 361 362 @Override 363 public ObjectAdapter asAdapter(JsonRepresentation repr) { 364 if (repr.isString()) { 365 final String dateStr = repr.asString(); 366 for (DateTimeFormatter formatter : formatters) { 367 try { 368 final LocalDateTime parsedDate = formatter.parseLocalDateTime(dateStr); 369 return adapterFor(parsedDate); 370 } catch (IllegalArgumentException ex) { 371 // fall through 372 } 373 } 374 } 375 return null; 376 } 377 378 @Override 379 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 380 final Object obj = unwrap(objectAdapter); 381 if(obj instanceof LocalDateTime) { 382 final LocalDateTime date = (LocalDateTime) obj; 383 final String dateStr = formatters.get(0).print(date.toDateTime()); 384 append(repr, dateStr, format, xIsisFormat); 385 } else { 386 append(repr, obj, format, xIsisFormat); 387 } 388 } 389 }); 390 391 putConverter(new JsonValueConverter("date-time", "jodadatetime", DateTime.class){ 392 393 final List<DateTimeFormatter> formatters = Arrays.asList( 394 JsonRepresentation.yyyyMMddTHHmmssZ, 395 DateTimeFormat.forPattern("yyyyMMdd'T'HHmmssZ"), 396 ISODateTimeFormat.basicDateTimeNoMillis(), 397 ISODateTimeFormat.basicDateTime() 398 ); 399 400 @Override 401 public ObjectAdapter asAdapter(JsonRepresentation repr) { 402 if (repr.isString()) { 403 final String dateStr = repr.asString(); 404 for (DateTimeFormatter formatter : formatters) { 405 try { 406 final DateTime parsedDate = formatter.parseDateTime(dateStr); 407 return adapterFor(parsedDate); 408 } catch (IllegalArgumentException ex) { 409 // fall through 410 } 411 } 412 } 413 return null; 414 } 415 416 @Override 417 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 418 final Object obj = unwrap(objectAdapter); 419 if(obj instanceof DateTime) { 420 final DateTime date = (DateTime) obj; 421 final String dateStr = formatters.get(0).print(date.toDateTime()); 422 append(repr, dateStr, format, xIsisFormat); 423 } else { 424 append(repr, obj, format, xIsisFormat); 425 } 426 } 427 }); 428 429 putConverter(new JsonValueConverter("date", "javautildate", java.util.Date.class){ 430 431 final List<DateTimeFormatter> formatters = Arrays.asList( 432 JsonRepresentation.yyyyMMddTHHmmssZ, 433 DateTimeFormat.forPattern("yyyyMMdd'T'HHmmssZ"), 434 ISODateTimeFormat.basicDateTimeNoMillis(), 435 ISODateTimeFormat.basicDateTime() 436 ); 437 @Override 438 public ObjectAdapter asAdapter(JsonRepresentation repr) { 439 if (repr.isString()) { 440 final String dateStr = repr.asString(); 441 for (DateTimeFormatter formatter : formatters) { 442 try { 443 final DateTime parseDateTime = formatter.parseDateTime(dateStr); 444 final java.util.Date parsedDate = parseDateTime.toDate(); 445 return adapterFor(parsedDate); 446 } catch (IllegalArgumentException ex) { 447 // fall through 448 } 449 } 450 } 451 return null; 452 } 453 454 @Override 455 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 456 final Object obj = unwrap(objectAdapter); 457 if(obj instanceof java.util.Date) { 458 final java.util.Date date = (java.util.Date) obj; 459 final String dateStr = formatters.get(0).print(new DateTime(date)); 460 append(repr, dateStr, format, xIsisFormat); 461 } else { 462 append(repr, obj, format, xIsisFormat); 463 } 464 } 465 }); 466 467 putConverter(new JsonValueConverter("date", "javasqldate", java.sql.Date.class){ 468 469 final List<DateTimeFormatter> formatters = Arrays.asList( 470 JsonRepresentation.yyyyMMdd, 471 DateTimeFormat.forPattern("yyyyMMdd"), 472 ISODateTimeFormat.basicDate() 473 ); 474 @Override 475 public ObjectAdapter asAdapter(JsonRepresentation repr) { 476 if (repr.isString()) { 477 final String dateStr = repr.asString(); 478 for (DateTimeFormatter formatter : formatters) { 479 try { 480 final DateTime parseDateTime = formatter.parseDateTime(dateStr); 481 final java.sql.Date parsedDate = new java.sql.Date(parseDateTime.getMillis()); 482 return adapterFor(parsedDate); 483 } catch (IllegalArgumentException ex) { 484 // fall through 485 } 486 } 487 } 488 return null; 489 } 490 491 @Override 492 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 493 final Object obj = unwrap(objectAdapter); 494 if(obj instanceof java.sql.Date) { 495 final java.sql.Date date = (java.sql.Date) obj; 496 final String dateStr = formatters.get(0).print(new DateTime(date)); 497 append(repr, dateStr, format, xIsisFormat); 498 } else { 499 append(repr, obj, format, xIsisFormat); 500 } 501 } 502 }); 503 504 putConverter(new JsonValueConverter("date", "javasqltime", java.sql.Time.class){ 505 506 final List<DateTimeFormatter> formatters = Arrays.asList( 507 JsonRepresentation._HHmmss, 508 DateTimeFormat.forPattern("HHmmss"), 509 ISODateTimeFormat.basicTime()); 510 @Override 511 public ObjectAdapter asAdapter(JsonRepresentation repr) { 512 if (repr.isString()) { 513 final String dateStr = repr.asString(); 514 for (DateTimeFormatter formatter : formatters) { 515 try { 516 final DateTime parseDateTime = formatter.parseDateTime(dateStr); 517 final java.sql.Time parsedTime = new java.sql.Time(parseDateTime.getMillis()); 518 return adapterFor(parsedTime); 519 } catch (IllegalArgumentException ex) { 520 // fall through 521 } 522 } 523 } 524 return null; 525 } 526 527 @Override 528 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 529 final Object obj = unwrap(objectAdapter); 530 if(obj instanceof java.sql.Time) { 531 final java.sql.Time date = (java.sql.Time) obj; 532 final String dateStr = formatters.get(0).print(new DateTime(date)); 533 append(repr, dateStr, format, xIsisFormat); 534 } else { 535 append(repr, obj, format, xIsisFormat); 536 } 537 } 538 }); 539 540 putConverter(new JsonValueConverter("utc-millisec", "javasqltimestamp", java.sql.Timestamp.class){ 541 542 @Override 543 public ObjectAdapter asAdapter(JsonRepresentation repr) { 544 if (repr.isLong()) { 545 final Long millis = repr.asLong(); 546 final java.sql.Timestamp parsedTimestamp = new java.sql.Timestamp(millis); 547 return adapterFor(parsedTimestamp); 548 } 549 if (repr.isString()) { 550 final String dateStr = repr.asString(); 551 try { 552 final Long parseMillis = Long.parseLong(dateStr); 553 final java.sql.Timestamp parsedTimestamp = new java.sql.Timestamp(parseMillis); 554 return adapterFor(parsedTimestamp); 555 } catch (IllegalArgumentException ex) { 556 // fall through 557 } 558 } 559 return null; 560 } 561 562 @Override 563 public void appendValueAndFormat(ObjectAdapter objectAdapter, JsonRepresentation repr) { 564 final Object obj = unwrap(objectAdapter); 565 if(obj instanceof java.sql.Timestamp) { 566 final java.sql.Timestamp date = (java.sql.Timestamp) obj; 567 final long millisStr = date.getTime(); 568 append(repr, millisStr, format, xIsisFormat); 569 } else { 570 append(repr, obj, format, xIsisFormat); 571 } 572 } 573 }); 574 } 575 576 577 578 public static ObjectAdapter asAdapter(final ObjectSpecification objectSpec, final JsonRepresentation argRepr) { 579 if (objectSpec == null) { 580 String reason = "ObjectSpec is null, cannot validate"; 581 argRepr.mapPut("invalidReason", reason); 582 throw new IllegalArgumentException(reason); 583 } 584 final EncodableFacet encodableFacet = objectSpec.getFacet(EncodableFacet.class); 585 if (encodableFacet == null) { 586 String reason = "ObjectSpec expected to have an EncodableFacet"; 587 argRepr.mapPut("invalidReason", reason); 588 throw new IllegalArgumentException(reason); 589 } 590 591 if(!argRepr.mapHas("value")) { 592 String reason = "No 'value' key"; 593 argRepr.mapPut("invalidReason", reason); 594 throw new IllegalArgumentException(reason); 595 } 596 final JsonRepresentation argValueRepr = argRepr.getRepresentation("value"); 597 if(argValueRepr == null) { 598 return null; 599 } 600 if (!argValueRepr.isValue()) { 601 String reason = "Representation must be of a value"; 602 argRepr.mapPut("invalidReason", reason); 603 throw new IllegalArgumentException(reason); 604 } 605 606 final JsonValueConverter jvc = converterBySpec.get(objectSpec.getSpecId()); 607 if(jvc == null) { 608 // best effort 609 if (argValueRepr.isString()) { 610 final String argStr = argValueRepr.asString(); 611 return encodableFacet.fromEncodedString(argStr); 612 } 613 614 final String reason = "Unable to parse value"; 615 argRepr.mapPut("invalidReason", reason); 616 throw new IllegalArgumentException(reason); 617 } 618 619 final ObjectAdapter asAdapter = jvc.asAdapter(argValueRepr); 620 if(asAdapter != null) { 621 return asAdapter; 622 } 623 624 // last attempt 625 if (argValueRepr.isString()) { 626 final String argStr = argValueRepr.asString(); 627 return encodableFacet.fromEncodedString(argStr); 628 } 629 630 final String reason = "Could not parse value '" + argValueRepr.asString() + "' as a " + objectSpec.getFullIdentifier(); 631 argRepr.mapPut("invalidReason", reason); 632 throw new IllegalArgumentException(reason); 633 } 634 635 public static void appendValueAndFormat(ObjectSpecification objectSpec, ObjectAdapter objectAdapter, JsonRepresentation repr) { 636 637 final JsonValueConverter jvc = converterBySpec.get(objectSpec.getSpecId()); 638 if(jvc != null) { 639 jvc.appendValueAndFormat(objectAdapter, repr); 640 } else { 641 final EncodableFacet encodableFacet = objectSpec.getFacet(EncodableFacet.class); 642 if (encodableFacet == null) { 643 throw new IllegalArgumentException("objectSpec expected to have EncodableFacet"); 644 } 645 Object value = objectAdapter != null? encodableFacet.toEncodedString(objectAdapter): NullNode.getInstance(); 646 append(repr, value, "string", "string"); 647 } 648 } 649 650 public static Object asObject(final ObjectAdapter objectAdapter) { 651 if (objectAdapter == null) { 652 throw new IllegalArgumentException("objectAdapter cannot be null"); 653 } 654 final ObjectSpecification objectSpec = objectAdapter.getSpecification(); 655 656 final JsonValueConverter jvc = converterBySpec.get(objectSpec.getSpecId()); 657 if(jvc != null) { 658 return jvc.asObject(objectAdapter); 659 } 660 661 // else 662 final EncodableFacet encodableFacet = objectSpec.getFacet(EncodableFacet.class); 663 if (encodableFacet == null) { 664 throw new IllegalArgumentException("objectSpec expected to have EncodableFacet"); 665 } 666 return encodableFacet.toEncodedString(objectAdapter); 667 } 668 669 670 671 private static void append(JsonRepresentation repr, Object value, String format, String xIsisFormat) { 672 repr.mapPut("value", value); 673 if(format != null) { 674 repr.mapPut("format", format); 675 } 676 if(xIsisFormat != null) { 677 repr.mapPut("extensions.x-isis-format", xIsisFormat); 678 } 679 } 680 681 private static void append(JsonRepresentation repr, ObjectAdapter value, String format, String xIsisFormat) { 682 append(repr, unwrap(value), format, xIsisFormat); 683 } 684 685 private static Object unwrap(ObjectAdapter objectAdapter) { 686 return objectAdapter != null? objectAdapter.getObject(): NullNode.getInstance(); 687 } 688 689 690 691 private static ObjectAdapter adapterFor(Object value) { 692 return getAdapterManager().adapterFor(value); 693 } 694 695 public static AdapterManager getAdapterManager() { 696 return IsisContext.getPersistenceSession().getAdapterManager(); 697 } 698 699}