/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.isis.viewer.restfulobjects.server.resources; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; import org.apache.isis.viewer.restfulobjects.applib.client.RestfulResponse; import org.apache.isis.viewer.restfulobjects.rendering.RendererContext; import org.apache.isis.viewer.restfulobjects.rendering.RestfulObjectsApplicationException; import org.apache.isis.viewer.restfulobjects.rendering.domainobjects.JsonValueEncoder; import org.apache.isis.viewer.restfulobjects.rendering.util.Util; import org.apache.isis.viewer.restfulobjects.server.util.OidUtils; /** * Utility class that encapsulates the logic for parsing some content (JSON, or a simple string that is JSON) * into an{@link org.apache.isis.core.metamodel.adapter.ObjectAdapter} of a specified * {@link org.apache.isis.core.metamodel.spec.ObjectSpecification type}. */ public class JsonParserHelper { private final static OidMarshaller OID_MARSHALLER = OidMarshaller.INSTANCE; private final static Pattern OBJECT_OID = Pattern.compile(".*objects\\/([^/]+)\\/(.+)"); private final RendererContext rendererContext; private final ObjectSpecification objectSpec; public JsonParserHelper( final RendererContext rendererContext, ObjectSpecification objectSpecification) { this.objectSpec = objectSpecification; this.rendererContext = rendererContext; } /** * @param bodyAsString * - as per {@link org.apache.isis.viewer.restfulobjects.rendering.util.Util#asStringUtf8(java.io.InputStream)} * @return */ ObjectAdapter parseAsMapWithSingleValue(final String bodyAsString) { final JsonRepresentation arguments = Util.readAsMap(bodyAsString); return parseAsMapWithSingleValue(arguments); } ObjectAdapter parseAsMapWithSingleValue(final JsonRepresentation arguments) { final JsonRepresentation representation = arguments.getRepresentation("value"); if (arguments.size() != 1 || representation == null) { throw RestfulObjectsApplicationException.createWithMessage(RestfulResponse.HttpStatusCode.BAD_REQUEST, "Body should be a map with a single key 'value' whose value represents an instance of type '%s'", resourceFor(objectSpec)); } return objectAdapterFor(arguments); } /** * * @param argRepr * - expected to be either a String or a Map (ie from within a * List, built by parsing a JSON structure). */ public ObjectAdapter objectAdapterFor(final JsonRepresentation argRepr) { if (argRepr == null) { return null; } if(!argRepr.mapHas("value")) { String reason = "No 'value' key"; argRepr.mapPut("invalidReason", reason); throw new IllegalArgumentException(reason); } if (objectSpec == null) { String reason = "ObjectSpec is null, cannot validate"; argRepr.mapPut("invalidReason", reason); throw new IllegalArgumentException(reason); } final JsonRepresentation argValueRepr = argRepr.getRepresentation("value"); // value (encodable) if (objectSpec.isEncodeable()) { try { return JsonValueEncoder.asAdapter(objectSpec, argValueRepr, null); }catch(IllegalArgumentException ex) { argRepr.mapPut("invalidReason", ex.getMessage()); throw ex; }catch(Exception ex) { StringBuilder buf = new StringBuilder("Failed to parse representation "); try { final String reprStr = argRepr.getString("value"); buf.append("'").append(reprStr).append("' "); } catch(Exception ex2) { } buf.append("as value of type '").append(objectSpec.getShortIdentifier()).append("'"); String reason = buf.toString(); argRepr.mapPut("invalidReason", reason); throw new IllegalArgumentException(reason); } } // reference if (!argValueRepr.isLink()) { final String reason = "Expected a link (because this object's type is not a value) but found no 'href'"; argRepr.mapPut("invalidReason", reason); throw new IllegalArgumentException(reason); } final String oidFromHref = encodedOidFromLink(argValueRepr); if (oidFromHref == null) { final String reason = "Could not parse 'href' to identify the object's OID"; argRepr.mapPut("invalidReason", reason); throw new IllegalArgumentException(reason); } final ObjectAdapter objectAdapter = OidUtils.getObjectAdapterElseNull(rendererContext, oidFromHref); if (objectAdapter == null) { final String reason = "'href' does not reference a known entity"; argRepr.mapPut("invalidReason", reason); throw new IllegalArgumentException(reason); } return objectAdapter; } static String encodedOidFromLink(final JsonRepresentation link) { final String href = link.getString("href"); final Matcher matcher = OBJECT_OID.matcher(href); if (!matcher.matches()) { return null; } String domainType = matcher.group(1); String instanceId = matcher.group(2); return OID_MARSHALLER.joinAsOid(domainType, instanceId); } private static String resourceFor(final ObjectSpecification objectSpec) { // TODO: should return a string in the form // http://localhost:8080/types/xxx return objectSpec.getFullIdentifier(); } }