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