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.server.arg; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.lang.reflect.*; 022import java.util.*; 023 024import org.apache.juneau.commons.reflect.*; 025import org.apache.juneau.http.*; 026import org.apache.juneau.marshall.*; 027import org.apache.juneau.marshall.httppart.*; 028import org.apache.juneau.rest.server.*; 029import org.apache.juneau.rest.server.httppart.*; 030 031/** 032 * Resolves method parameters annotated with {@link PathRemainder} on {@link RestOp}-annotated Java methods. 033 * 034 * <p> 035 * This is a specialized version of {@link PathArg} for the path remainder (the part matched by {@code /*}). 036 * It's functionally equivalent to using {@code @Path("/*")}, but provides a more intuitive annotation name. 037 * 038 * <p> 039 * The parameter value is resolved using: 040 * <p class='bjava'> 041 * <jv>opSession</jv> 042 * .{@link RestOpSession#getRequest() getRequest}() 043 * .{@link RestRequest#getPathParams() getPathParams}() 044 * .{@link RequestPathParamList#get(String) get}(<js>"/*"</js>) 045 * .{@link RequestPathParam#as(Class) as}(<jv>type</jv>); 046 * </p> 047 * 048 * <p> 049 * {@link HttpPartSchema schema} is derived from the {@link PathRemainder} annotation. 050 * 051 * <h5 class='section'>See Also:</h5><ul> 052 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JavaMethodParameters">Java Method Parameters</a> 053 * <li class='ja'>{@link PathRemainder} 054 * <li class='jc'>{@link PathArg} 055 * </ul> 056 * 057 * @since 9.2.0 058 */ 059public class PathRemainderArg implements RestOpArg { 060 061 private static final AnnotationProvider AP = AnnotationProvider.INSTANCE; 062 063 /** 064 * Static creator. 065 * 066 * @param paramInfo The Java method parameter being resolved. Must not be <jk>null</jk>. 067 * @param annotations The annotations to apply to any new part parsers. 068 * @return A new {@link PathRemainderArg}, or <jk>null</jk> if the parameter is not annotated with {@link PathRemainder}. 069 */ 070 public static PathRemainderArg create(ParameterInfo paramInfo, AnnotationWorkList annotations) { 071 if (AP.has(PathRemainder.class, paramInfo)) 072 return new PathRemainderArg(paramInfo, annotations); 073 return null; 074 } 075 076 private static Optional<String> findDef(ParameterInfo pi) { 077 // @formatter:off 078 return AP.find(PathRemainder.class, pi) 079 .stream() 080 .map(AnnotationInfo::inner) 081 .filter(x -> ine(x.def())) 082 .findFirst() 083 .map(PathRemainder::def); 084 // @formatter:on 085 } 086 087 private final HttpPartParser partParser; 088 private final HttpPartSchema schema; 089 private final String def; 090 091 private final Type type; 092 093 /** 094 * Constructor. 095 * 096 * @param paramInfo The Java method parameter being resolved. Must not be <jk>null</jk>. 097 * @param annotations The annotations to apply to any new part parsers. 098 */ 099 protected PathRemainderArg(ParameterInfo paramInfo, AnnotationWorkList annotations) { 100 this.def = findDef(paramInfo).orElse(null); 101 this.type = paramInfo.getParameterType().innerType(); 102 this.schema = HttpPartSchema.create(PathRemainder.class, paramInfo); 103 @SuppressWarnings({ 104 "unchecked" // Type erasure on reflective/generic cast; element type is verified at call site 105 }) 106 var pp = (Class<? extends HttpPartParser>)schema.getParser(); 107 this.partParser = nn(pp) ? HttpPartParser.creator().type(pp).apply(annotations).create() : null; 108 } 109 110 @Override /* Overridden from RestOpArg */ 111 public Object resolve(RestOpSession opSession) throws Exception { 112 var req = opSession.getRequest(); 113 var ps = partParser == null ? req.getPartParserSession() : partParser.getPartSession(); 114 // The path remainder is stored under the name "/*" 115 return req.getPathParams().get("/*").parser(ps).schema(schema).def(def).as(type).orElse(null); 116 } 117}