/**
* 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();
}
}