JsonUtil.java
- /***************************************************************************
- Copyright 2013 Emily Estes
- Licensed 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 net.metanotion.web.concrete;
- import java.lang.annotation.Annotation;
- import java.lang.reflect.AnnotatedElement;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
- import javax.inject.Named;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import net.metanotion.functor.Block;
- import net.metanotion.io.ClassLoaderFileSystem;
- import net.metanotion.io.File;
- import net.metanotion.io.FileSystem;
- import net.metanotion.json.JsonArray;
- import net.metanotion.json.JsonObject;
- import net.metanotion.util.Json;
- import net.metanotion.util.OutputWrapperFilter;
- import net.metanotion.util.Pair;
- import net.metanotion.util.Wrap;
- import net.metanotion.util.types.JsonP;
- import net.metanotion.util.types.ToJsonMagicP;
- import net.metanotion.util.types.Parser;
- import net.metanotion.web.HttpAny;
- import net.metanotion.web.HttpDelete;
- import net.metanotion.web.HttpGet;
- import net.metanotion.web.HttpHead;
- import net.metanotion.web.HttpNone;
- import net.metanotion.web.HttpPost;
- import net.metanotion.web.HttpPut;
- import net.metanotion.web.HttpStatus;
- import net.metanotion.web.HttpValues;
- import net.metanotion.web.Param;
- import net.metanotion.web.Response;
- import net.metanotion.web.Request;
- public final class JsonUtil {
- private static final Logger logger = LoggerFactory.getLogger(JsonUtil.class);
- private static void makeAPIMethod(JsonObject api, String name, String prefix, AnnotatedElement e) {
- final JsonObject m = new JsonObject();
- final Annotation[] ann = e.getAnnotations();
- m.put("uri", prefix + name);
- if(e.isAnnotationPresent(HttpAny.class)) {
- return; //m.put("method", "POST");
- } else {
- for(final Annotation a: ann) {
- if(a instanceof HttpDelete) { m.put("method", "DELETE"); }
- if(a instanceof HttpGet) { m.put("method", "GET"); }
- if(a instanceof HttpHead) { m.put("method", "HEAD"); }
- if(a instanceof HttpPost) { m.put("method", "POST"); }
- if(a instanceof HttpPut) { m.put("method", "PUT"); }
- }
- }
- // do result mime type
- for(final Annotation a: ann) {
- if(a instanceof Json) {
- m.put("result", "json");
- } else if(a instanceof Response) {
- final Param[] ps = ((Response) a).value();
- for(Param p: ps) {
- if("mime".equals(p.name())) {
- m.put("result", p.value());
- }
- }
- }
- if(a instanceof Request) {
- final Param[] ps = ((Request) a).value();
- for(Param p: ps) {
- if("mime".equals(p.name())) {
- m.put("mime", p.value());
- }
- }
- }
- }
- api.put(name, m);
- }
- public static JsonObject makeWebApi(final Class klass, final String prefix) {
- return makeWebApi(klass, prefix, new HashMap<String,Object>());
- }
- public static JsonObject makeWebApi(final Class klass, final String prefix, final Map<String,Object> extraFields) {
- final JsonObject api = new JsonObject();
- for(final Field f: klass.getFields()) {
- if(f.isAnnotationPresent(HttpNone.class)) { continue; }
- final Named n = f.getAnnotation(Named.class);
- makeAPIMethod(api, n==null ? f.getName() : n.value(), prefix, f);
- }
- // method, uri = prefix
- for(final Method m: klass.getMethods()) {
- if(m.isAnnotationPresent(HttpNone.class)) { continue; }
- final Named n = m.getAnnotation(Named.class);
- makeAPIMethod(api, n==null ? m.getName() : n.value(), prefix, m);
- }
- for(final Map.Entry<String,Object> element: extraFields.entrySet()) {
- api.put(element.getKey(), element.getValue());
- }
- return api;
- }
- public static FileSystem<File> getJavaScript() {
- return new ClassLoaderFileSystem(JsonUtil.class.getClassLoader(), "js");
- }
- private static final class JsonTransformer implements Block<Object,Object> {
- private final Parser p;
- public JsonTransformer(final Parser p) { this.p = p; }
- @Override public Object eval(final Object o) throws Exception {
- logger.debug("Transform: {}", o);
- if(o == null) { return null; }
- if((String.class.equals(o.getClass())) && ("".equals((String) o))) { return ""; }
- try {
- return p.parse(o);
- } catch (final Exception e) {
- logger.debug("Error!", e);
- return null;
- }
- }
- }
- /** An empty list of headers. */
- private static final List<Map.Entry<String,Object>> EMPTY_LIST =
- Collections.unmodifiableList(new LinkedList<Map.Entry<String,Object>>());
- /** JSON responses need the Content-Type set to JSON. */
- private static final List<Map.Entry<String,Object>> JSON_HEADER_LIST =
- Collections.unmodifiableList(Arrays.asList((Map.Entry<String,Object>)
- new Pair<String,Object>("Content-Type", "application/json; charset=utf-8")));
- private static final class CheckWrap implements Block<Object, Object> {
- private final Block wrapped;
- private final Block unwrapped;
- public CheckWrap(Block wrapped, Block unwrapped) {
- this.wrapped = wrapped;
- this.unwrapped = unwrapped;
- }
- @Override public Object eval(final Object o) throws Exception {
- logger.debug("CheckWrap: {}", o);
- final Object result = (o instanceof Wrap) ? wrapped.eval(o) : unwrapped.eval(o);
- if(result instanceof HttpValues) {
- logger.debug("wrapped {}", ((HttpValues) result).unwrap());
- return new DelegatingHttpValues(JsonUtil.JSON_HEADER_LIST, JsonUtil.EMPTY_LIST, (HttpValues) result);
- } else {
- logger.debug("unwrapped {}", result);
- return new SimpleHttpValues(JsonUtil.JSON_HEADER_LIST, JsonUtil.EMPTY_LIST, result, HttpStatus.OK.codeNumber());
- }
- }
- }
- private static final class ToJson implements Parser<JsonObject> {
- private final Map<Class, Parser<JsonObject>> map = new HashMap<>();
- @Override public JsonObject parse(final Object o) throws Exception {
- final Class c = o.getClass();
- logger.debug("Parsing {} - {}", c, o);
- Parser<JsonObject> m = map.get(c);
- if(m == null) {
- logger.debug("no parser");
- if(JsonP.parseable(c)) {
- logger.debug("Parseable Json object");
- m = JsonP.INSTANCE;
- } else {
- logger.debug("Reflectively generated Json object");
- m = new ToJsonMagicP(c);
- }
- map.put(c,m);
- }
- logger.debug("parsing object now");
- return m.parse(o);
- }
- @Override public Class<JsonObject> outputInterface() { return JsonObject.class; }
- }
- private static Block getResultXfmr(final Class retType) {
- logger.debug("Result type is {}", retType);
- if(!JsonObject.class.isAssignableFrom(retType)) {
- Parser p = null;
- if(JsonP.parseable(retType)) {
- logger.debug("parseable as a JSON Object");
- p = JsonP.INSTANCE;
- } else {
- logger.debug("ToJson");
- p = new ToJson();
- }
- return new JsonTransformer(p);
- }
- return null;
- }
- public static Map<String,Block> generateJsonResultTransformers(final Class klazz) {
- final Map<String, Block> map = new HashMap<>();
- for(final Field f: klazz.getFields()) {
- if(f.getAnnotation(Json.class) != null) {
- final Block tx = getResultXfmr(f.getType());
- if(tx != null) {
- map.put(f.getName(),new CheckWrap(new OutputWrapperFilter(tx), tx));
- }
- }
- }
- for(final Method m: klazz.getMethods()) {
- if(m.getAnnotation(Json.class) != null) {
- final Block tx = getResultXfmr(m.getReturnType());
- if(tx != null) {
- map.put(m.getName(),new CheckWrap(new OutputWrapperFilter(tx), tx));
- }
- }
- }
- return map;
- }
- }