/** * 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.jooby.internal; import static javaslang.API.$; import static javaslang.API.Case; import static javaslang.API.Match; import static javaslang.Predicates.instanceOf; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import org.jooby.Err; import org.jooby.MediaType; import org.jooby.Mutant; import org.jooby.Parser; import org.jooby.Status; import org.jooby.internal.parser.ParserExecutor; import com.google.common.collect.ImmutableMap; import com.google.inject.TypeLiteral; import javaslang.Tuple; import javaslang.Tuple3; /** * Default mutant implementation. * * NOTE: This class isn't thread-safe. * * @author edgar */ public class MutantImpl implements Mutant { private static final String REQUIRED = "Required %s is not present"; private static final String FAILURE = "Failed to parse %s to '%s'"; private final Map<Object, Object> results = new HashMap<>(1); private final ParserExecutor parser; private Object data; private MediaType type; public MutantImpl(final ParserExecutor parser, final MediaType type, final Object data) { this.parser = parser; this.type = type; this.data = data; } public MutantImpl(final ParserExecutor parser, final Object data) { this(parser, MediaType.plain, data); } @Override public <T> T to(final TypeLiteral<T> type) { return to(type, this.type); } @SuppressWarnings("unchecked") @Override public <T> T to(final TypeLiteral<T> type, final MediaType mtype) { T result = (T) results.get(type); if (result == null) { try { result = parser.convert(type, mtype, data); if (result == ParserExecutor.NO_PARSER) { Tuple3<String, String, Status> md = md(); throw new Err(md._3, String.format(FAILURE, md._2, type)); } results.put(type, result); } catch (NoSuchElementException ex) { Tuple3<String, String, Status> md = md(); throw new Err.Missing(String.format(REQUIRED, md._2)); } catch (Err ex) { throw ex; } catch (Throwable ex) { Tuple3<String, String, Status> md = md(); throw new Err(parser.statusCode(ex), String.format(FAILURE, md._2, type), ex); } } return result; } @SuppressWarnings("unchecked") @Override public Map<String, Mutant> toMap() { if (data instanceof Map) { return (Map<String, Mutant>) data; } return ImmutableMap.of(md()._1, this); } @SuppressWarnings("rawtypes") @Override public boolean isSet() { if (data instanceof ParamReferenceImpl) { return ((ParamReferenceImpl) data).size() > 0; } if (data instanceof Parser.BodyReference) { return ((Parser.BodyReference) data).length() > 0; } return ((Map) data).size() > 0; } private Tuple3<String, String, Status> md() { return Match(data).of( Case(instanceOf(ParamReferenceImpl.class), p -> Tuple.of(p.name(), p.type() + " '" + p.name() + "'", Status.BAD_REQUEST)), Case(instanceOf(Parser.BodyReference.class), Tuple.of("body", "body", Status.UNSUPPORTED_MEDIA_TYPE)), Case($(), Tuple.of("params", "parameters", Status.BAD_REQUEST))); } @Override public String toString() { return data.toString(); } }