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.http.annotation.PathAnnotation.*; 020 021import java.lang.reflect.*; 022 023import org.apache.juneau.*; 024import org.apache.juneau.collections.*; 025import org.apache.juneau.common.utils.*; 026import org.apache.juneau.http.annotation.*; 027import org.apache.juneau.httppart.*; 028import org.apache.juneau.reflect.*; 029import org.apache.juneau.rest.*; 030import org.apache.juneau.rest.annotation.*; 031import org.apache.juneau.rest.httppart.*; 032import org.apache.juneau.rest.util.*; 033 034/** 035 * Resolves method parameters and parameter types annotated with {@link Path} on {@link RestOp}-annotated Java methods. 036 * 037 * <p> 038 * The parameter value is resolved using: 039 * <p class='bjava'> 040 * <jv>opSession</jv> 041 * .{@link RestOpSession#getRequest() getRequest}() 042 * .{@link RestRequest#getPathParams() getPathParams}() 043 * .{@link RequestPathParams#get(String) get}(<jv>name</jv>) 044 * .{@link RequestPathParam#as(Class) as}(<jv>type</jv>); 045 * </p> 046 * 047 * <p> 048 * {@link HttpPartSchema schema} is derived from the {@link Path} annotation. 049 * 050 * <h5 class='section'>See Also:</h5><ul> 051 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JavaMethodParameters">Java Method Parameters</a> 052 * </ul> 053 */ 054public class PathArg implements RestOpArg { 055 private final HttpPartParser partParser; 056 private final HttpPartSchema schema; 057 private final String name, def; 058 private final Type type; 059 060 /** 061 * Static creator. 062 * 063 * @param paramInfo The Java method parameter being resolved. 064 * @param annotations The annotations to apply to any new part parsers. 065 * @param pathMatcher Path matcher for the specified method. 066 * @return A new {@link PathArg}, or <jk>null</jk> if the parameter is not annotated with {@link Path}. 067 */ 068 public static PathArg create(ParamInfo paramInfo, AnnotationWorkList annotations, UrlPathMatcher pathMatcher) { 069 if (paramInfo.hasAnnotation(Path.class) || paramInfo.getParameterType().hasAnnotation(Path.class)) 070 return new PathArg(paramInfo, annotations, pathMatcher); 071 return null; 072 } 073 074 /** 075 * Constructor. 076 * 077 * @param paramInfo The Java method parameter being resolved. 078 * @param annotations The annotations to apply to any new part parsers. 079 * @param pathMatcher Path matcher for the specified method. 080 */ 081 protected PathArg(ParamInfo paramInfo, AnnotationWorkList annotations, UrlPathMatcher pathMatcher) { 082 this.name = getName(paramInfo, pathMatcher); 083 this.def = findDef(paramInfo).orElse(null); 084 this.type = paramInfo.getParameterType().innerType(); 085 this.schema = HttpPartSchema.create(Path.class, paramInfo); 086 Class<? extends HttpPartParser> pp = schema.getParser(); 087 this.partParser = pp != null ? HttpPartParser.creator().type(pp).apply(annotations).create() : null; 088 } 089 090 private String getName(ParamInfo pi, UrlPathMatcher pathMatcher) { 091 String p = findName(pi).orElse(null); 092 if (p != null) 093 return p; 094 if (pathMatcher != null) { 095 int idx = 0; 096 int i = pi.getIndex(); 097 MethodInfo mi = pi.getMethod(); 098 099 for (int j = 0; j < i; j++) 100 if (mi.getParam(i).getAnnotation(Path.class) != null) 101 idx++; 102 103 String[] vars = pathMatcher.getVars(); 104 if (vars.length <= idx) 105 throw new ArgException(pi, "Number of attribute parameters exceeds the number of URL pattern variables"); 106 107 // Check for {#} variables. 108 String idxs = String.valueOf(idx); 109 for (String var : vars) 110 if (StringUtils.isNumeric(var) && var.equals(idxs)) 111 return var; 112 113 return pathMatcher.getVars()[idx]; 114 } 115 throw new ArgException(pi, "@Path used without name or value"); 116 } 117 118 @Override /* RestOpArg */ 119 public Object resolve(RestOpSession opSession) throws Exception { 120 RestRequest req = opSession.getRequest(); 121 if (name.equals("*")) { 122 JsonMap m = new JsonMap(); 123 req.getPathParams().stream().forEach(x -> m.put(x.getName(), x.getValue())); 124 return req.getBeanSession().convertToType(m, type); 125 } 126 HttpPartParserSession ps = partParser == null ? req.getPartParserSession() : partParser.getPartSession(); 127 return req.getPathParams().get(name).parser(ps).schema(schema).def(def).as(type).orElse(null); 128 } 129}