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