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