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}