001// Generated by delombok at Thu Mar 19 15:24:58 GMT 2020 002/* 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020package org.apache.isis.viewer.restfulobjects.rendering.domainobjects; 021 022import java.util.List; 023import java.util.Map; 024import java.util.function.Function; 025import java.util.stream.Collectors; 026import javax.annotation.PostConstruct; 027import javax.inject.Inject; 028import javax.inject.Named; 029import javax.inject.Singleton; 030import com.fasterxml.jackson.databind.node.NullNode; 031import org.springframework.beans.factory.annotation.Qualifier; 032import org.springframework.context.annotation.Primary; 033import org.springframework.core.annotation.Order; 034import org.springframework.stereotype.Service; 035import org.apache.isis.applib.annotation.OrderPrecedence; 036import org.apache.isis.core.commons.internal.base._NullSafe; 037import org.apache.isis.core.commons.internal.collections._Maps; 038import org.apache.isis.core.commons.internal.exceptions._Exceptions; 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.ManagedObject; 042import org.apache.isis.core.metamodel.spec.ObjectSpecId; 043import org.apache.isis.core.metamodel.spec.ObjectSpecification; 044import org.apache.isis.core.metamodel.specloader.SpecificationLoader; 045import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 046import static org.apache.isis.core.commons.internal.base._With.requires; 047 048/** 049 * Similar to Isis' value encoding, but with additional support for JSON 050 051 * primitives. 052 */ 053@Service 054@Named("isisRoRendering.JsonValueEncoder") 055@Order(OrderPrecedence.EARLY) 056@Primary 057@Qualifier("Default") 058@Singleton 059public class JsonValueEncoder { 060 @Inject 061 private SpecificationLoader specificationLoader; 062 063 @PostConstruct 064 public void init() { 065 //XXX no lombok val here 066 Function<Object, ManagedObject> pojoToAdapter = pojo -> ManagedObject.of(specificationLoader::loadSpecification, pojo); 067 new JsonValueEncoder_Converters().asList(pojoToAdapter).forEach(this::registerConverter); 068 } 069 070 private Map<ObjectSpecId, JsonValueConverter> converterBySpecId = _Maps.newLinkedHashMap(); 071 072 private void registerConverter(JsonValueConverter jvc) { 073 for (final org.apache.isis.core.metamodel.spec.ObjectSpecId specId : jvc.getSpecIds()) { 074 converterBySpecId.put(specId, jvc); 075 } 076 } 077 078 public ManagedObject asAdapter(final ObjectSpecification objectSpec, final JsonRepresentation argValueRepr, final String format) { 079 if (argValueRepr == null) { 080 return null; 081 } 082 if (objectSpec == null) { 083 throw new IllegalArgumentException("ObjectSpecification is required"); 084 } 085 if (!argValueRepr.isValue()) { 086 throw new IllegalArgumentException("Representation must be of a value"); 087 } 088 final EncodableFacet encodableFacet = objectSpec.getFacet(EncodableFacet.class); 089 if (encodableFacet == null) { 090 String reason = "ObjectSpec expected to have an EncodableFacet"; 091 throw new IllegalArgumentException(reason); 092 } 093 final ObjectSpecId specId = objectSpec.getSpecId(); 094 final JsonValueConverter jvc = converterBySpecId.get(specId); 095 if (jvc == null) { 096 // best effort 097 if (argValueRepr.isString()) { 098 final String argStr = argValueRepr.asString(); 099 return encodableFacet.fromEncodedString(argStr); 100 } 101 throw new IllegalArgumentException("Unable to parse value"); 102 } 103 final ManagedObject asAdapter = jvc.asAdapter(argValueRepr, format); 104 if (asAdapter != null) { 105 return asAdapter; 106 } 107 // last attempt 108 if (argValueRepr.isString()) { 109 final String argStr = argValueRepr.asString(); 110 try { 111 return encodableFacet.fromEncodedString(argStr); 112 } catch (TextEntryParseException ex) { 113 throw new IllegalArgumentException(ex.getMessage()); 114 } 115 } 116 throw new IllegalArgumentException("Could not parse value \'" + argValueRepr.asString() + "\' as a " + objectSpec.getFullIdentifier()); 117 } 118 119 public Object appendValueAndFormat(ManagedObject objectAdapter, ObjectSpecification objectSpecification, JsonRepresentation repr, String format, boolean suppressExtensions) { 120 final org.apache.isis.core.metamodel.spec.ObjectSpecId specId = objectSpecification.getSpecId(); 121 final org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueEncoder.JsonValueConverter jsonValueConverter = converterBySpecId.get(specId); 122 if (jsonValueConverter != null) { 123 return jsonValueConverter.appendValueAndFormat(objectAdapter, format, repr, suppressExtensions); 124 } else { 125 final org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet encodableFacet = objectSpecification.getFacet(EncodableFacet.class); 126 if (encodableFacet == null) { 127 throw _Exceptions.illegalArgument("objectSpec \'%s\' expected to have EncodableFacet or a registered JsonValueConverter", specId); 128 } 129 Object value = objectAdapter != null ? encodableFacet.toEncodedString(objectAdapter) : NullNode.getInstance(); 130 repr.mapPut("value", value); 131 appendFormats(repr, "string", "string", suppressExtensions); 132 return value; 133 } 134 } 135 136 public Object asObject(final ManagedObject adapter, final String format) { 137 requires(adapter, "adapter"); 138 final org.apache.isis.core.metamodel.spec.ObjectSpecification objectSpec = adapter.getSpecification(); 139 final org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueEncoder.JsonValueConverter jsonValueConverter = converterBySpecId.get(objectSpec.getSpecId()); 140 if (jsonValueConverter != null) { 141 return jsonValueConverter.asObject(adapter, format); 142 } 143 // else 144 final org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet encodableFacet = objectSpec.getFacet(EncodableFacet.class); 145 if (encodableFacet == null) { 146 throw new IllegalArgumentException("objectSpec expected to have EncodableFacet"); 147 } 148 return encodableFacet.toEncodedString(adapter); 149 } 150 151 static void appendFormats(JsonRepresentation repr, String format, String xIsisFormat, boolean suppressExtensions) { 152 if (format != null) { 153 repr.mapPut("format", format); 154 } 155 if (!suppressExtensions && xIsisFormat != null) { 156 repr.mapPut("extensions.x-isis-format", xIsisFormat); 157 } 158 } 159 160 static Object unwrapAsObjectElseNullNode(ManagedObject adapter) { 161 return adapter != null ? adapter.getPojo() : NullNode.getInstance(); 162 } 163 164// ManagedObject adapterFor(Object pojo) { 165// return objectAdapterProvider.adapterFor(pojo); 166// } 167 // -- NESTED TYPE DECLARATIONS 168 public static class ExpectedStringRepresentingValueException extends IllegalArgumentException { 169 private static final long serialVersionUID = 1L; 170 } 171 172 173 public static abstract class JsonValueConverter { 174 protected final String format; 175 protected final String xIsisFormat; 176 private final Class<?>[] classes; 177 178 public JsonValueConverter(String format, String xIsisFormat, Class<?>... classes) { 179 this.format = format; 180 this.xIsisFormat = xIsisFormat; 181 this.classes = classes; 182 } 183 184 public List<ObjectSpecId> getSpecIds() { 185 return _NullSafe.stream(classes).map((Class<?> cls) -> ObjectSpecId.of(cls.getName())).collect(Collectors.toList()); 186 } 187 188 /** 189 * The value, otherwise <tt>null</tt>. 190 */ 191 public abstract ManagedObject asAdapter(JsonRepresentation repr, String format); 192 193 public Object appendValueAndFormat(ManagedObject objectAdapter, String format, JsonRepresentation repr, boolean suppressExtensions) { 194 final Object value = unwrapAsObjectElseNullNode(objectAdapter); 195 repr.mapPut("value", value); 196 appendFormats(repr, this.format, this.xIsisFormat, suppressExtensions); 197 return value; 198 } 199 200 public Object asObject(ManagedObject objectAdapter, String format) { 201 return objectAdapter.getPojo(); 202 } 203 } 204 205 /** 206 * JUnit support 207 */ 208 public static JsonValueEncoder forTesting(SpecificationLoader specificationLoader) { 209 final org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueEncoder jsonValueEncoder = new JsonValueEncoder(); 210 jsonValueEncoder.specificationLoader = specificationLoader; 211 jsonValueEncoder.init(); 212 return jsonValueEncoder; 213 } 214}