001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.arg; 018 019import static org.apache.juneau.common.utils.StringUtils.*; 020import static org.apache.juneau.common.utils.Utils.*; 021import static org.apache.juneau.http.annotation.FormDataAnnotation.*; 022 023import java.util.*; 024 025import org.apache.juneau.*; 026import org.apache.juneau.annotation.*; 027import org.apache.juneau.collections.*; 028import org.apache.juneau.http.annotation.*; 029import org.apache.juneau.httppart.*; 030import org.apache.juneau.internal.*; 031import org.apache.juneau.reflect.*; 032import org.apache.juneau.rest.*; 033import org.apache.juneau.rest.annotation.*; 034import org.apache.juneau.rest.httppart.*; 035 036/** 037 * Resolves method parameters and parameter types annotated with {@link FormData} on {@link RestOp}-annotated Java methods. 038 * 039 * <p> 040 * The parameter value is resolved using: 041 * <p class='bjava'> 042 * <jv>opSession</jv> 043 * .{@link RestOpSession#getRequest() getRequest}() 044 * .{@link RestRequest#getFormParams() getFormParams}() 045 * .{@link RequestFormParams#get(String) get}(<jv>name</jv>) 046 * .{@link RequestFormParam#as(Class) as}(<jv>type</jv>); 047 * </p> 048 * 049 * <p> 050 * {@link HttpPartSchema schema} is derived from the {@link FormData} annotation. 051 * 052 * <p> 053 * If the {@link Schema#collectionFormat()} value is {@link HttpPartCollectionFormat#MULTI}, then the data type can be a {@link Collection} or array. 054 * 055 * <h5 class='section'>See Also:</h5><ul> 056 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JavaMethodParameters">Java Method Parameters</a> 057 * </ul> 058 */ 059public class FormDataArg implements RestOpArg { 060 private final boolean multi; 061 private final HttpPartParser partParser; 062 private final HttpPartSchema schema; 063 private final String name, def; 064 private final ClassInfo type; 065 066 /** 067 * Static creator. 068 * 069 * @param paramInfo The Java method parameter being resolved. 070 * @param annotations The annotations to apply to any new part parsers. 071 * @return A new {@link FormDataArg}, or <jk>null</jk> if the parameter is not annotated with {@link FormData}. 072 */ 073 public static FormDataArg create(ParamInfo paramInfo, AnnotationWorkList annotations) { 074 if (paramInfo.hasAnnotation(FormData.class) || paramInfo.getParameterType().hasAnnotation(FormData.class)) 075 return new FormDataArg(paramInfo, annotations); 076 return null; 077 } 078 079 /** 080 * Constructor. 081 * 082 * @param pi The Java method parameter being resolved. 083 * @param annotations The annotations to apply to any new part parsers. 084 */ 085 protected FormDataArg(ParamInfo pi, AnnotationWorkList annotations) { 086 this.name = findName(pi).orElseThrow(()->new ArgException(pi, "@FormData used without name or value")); 087 this.def = findDef(pi).orElse(null); 088 this.type = pi.getParameterType(); 089 this.schema = HttpPartSchema.create(FormData.class, pi); 090 Class<? extends HttpPartParser> pp = schema.getParser(); 091 this.partParser = pp != null ? HttpPartParser.creator().type(pp).apply(annotations).create() : null; 092 this.multi = schema.getCollectionFormat() == HttpPartCollectionFormat.MULTI; 093 094 if (multi && ! type.isCollectionOrArray()) 095 throw new ArgException(pi, "Use of multipart flag on @FormData parameter that is not an array or Collection"); 096 } 097 098 @SuppressWarnings({ "rawtypes", "unchecked" }) 099 @Override /* RestOpArg */ 100 public Object resolve(RestOpSession opSession) throws Exception { 101 RestRequest req = opSession.getRequest(); 102 HttpPartParserSession ps = partParser == null ? req.getPartParserSession() : partParser.getPartSession(); 103 RequestFormParams rh = req.getFormParams(); 104 BeanSession bs = req.getBeanSession(); 105 ClassMeta<?> cm = bs.getClassMeta(type.innerType()); 106 107 if (multi) { 108 Collection c = cm.isArray() ? list() : (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new JsonList()); 109 rh.getAll(name).stream().map(x -> x.parser(ps).schema(schema).as(cm.getElementType()).orElse(null)).forEach(x -> c.add(x)); 110 return cm.isArray() ? ArrayUtils.toArray(c, cm.getElementType().getInnerClass()) : c; 111 } 112 113 if (cm.isMapOrBean() && isOneOf(name, "*", "")) { 114 JsonMap m = new JsonMap(); 115 rh.forEach(e -> m.put(e.getName(), e.parser(ps).schema(schema == null ? null : schema.getProperty(e.getName())).as(cm.getValueType()).orElse(null))); 116 return req.getBeanSession().convertToType(m, cm); 117 } 118 119 return rh.getLast(name).parser(ps).schema(schema).def(def).as(type.innerType()).orElse(null); 120 } 121}