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.HeaderAnnotation.*;
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.http.header.*;
026import org.apache.juneau.httppart.*;
027import org.apache.juneau.internal.*;
028import org.apache.juneau.reflect.*;
029import org.apache.juneau.rest.*;
030import org.apache.juneau.rest.annotation.*;
031import org.apache.juneau.rest.httppart.*;
032
033/**
034 * Resolves method parameters and parameter types annotated with {@link Header} on {@link RestOp}-annotated Java methods.
035 *
036 * <p>
037 * This includes any of the following predefined request header types:
038 *
039 * <ul class='javatree condensed'>
040 *    <li class='jc'>{@link Accept}
041 *    <li class='jc'>{@link AcceptCharset}
042 *    <li class='jc'>{@link AcceptEncoding}
043 *    <li class='jc'>{@link AcceptLanguage}
044 *    <li class='jc'>{@link AcceptRanges}
045 *    <li class='jc'>{@link Authorization}
046 *    <li class='jc'>{@link CacheControl}
047 *    <li class='jc'>{@link ClientVersion}
048 *    <li class='jc'>{@link Connection}
049 *    <li class='jc'>{@link ContentDisposition}
050 *    <li class='jc'>{@link ContentEncoding}
051 *    <li class='jc'>{@link ContentLength}
052 *    <li class='jc'>{@link ContentType}
053 *    <li class='jc'>{@link org.apache.juneau.http.header.Date}
054 *    <li class='jc'>{@link Debug}
055 *    <li class='jc'>{@link Expect}
056 *    <li class='jc'>{@link Forwarded}
057 *    <li class='jc'>{@link From}
058 *    <li class='jc'>{@link Host}
059 *    <li class='jc'>{@link IfMatch}
060 *    <li class='jc'>{@link IfModifiedSince}
061 *    <li class='jc'>{@link IfNoneMatch}
062 *    <li class='jc'>{@link IfRange}
063 *    <li class='jc'>{@link IfUnmodifiedSince}
064 *    <li class='jc'>{@link MaxForwards}
065 *    <li class='jc'>{@link NoTrace}
066 *    <li class='jc'>{@link Origin}
067 *    <li class='jc'>{@link Pragma}
068 *    <li class='jc'>{@link ProxyAuthorization}
069 *    <li class='jc'>{@link Range}
070 *    <li class='jc'>{@link Referer}
071 *    <li class='jc'>{@link TE}
072 *    <li class='jc'>{@link Thrown}
073 *    <li class='jc'>{@link Upgrade}
074 *    <li class='jc'>{@link UserAgent}
075 *    <li class='jc'>{@link Warning}
076 * </ul>
077 *
078 * <p>
079 * The parameter value is resolved using:
080 * <p class='bjava'>
081 *    <jv>opSession</jv>
082 *       .{@link RestOpSession#getRequest() getRequest}()
083 *       .{@link RestRequest#getHeaders() getHeaders}();
084 * </p>
085 *
086 * <p>
087 * {@link HttpPartSchema schema} is derived from the {@link Header} annotation.
088 *
089 * <p>
090 * If the {@link Schema#collectionFormat()} value is {@link HttpPartCollectionFormat#MULTI}, then the data type can be a {@link Collection} or array.
091 *
092 * <h5 class='section'>See Also:</h5><ul>
093 *    <li class='link'><a class="doclink" href="../../../../../index.html#jrs.JavaMethodParameters">Java Method Parameters</a>
094 * </ul>
095 */
096public class HeaderArg implements RestOpArg {
097   private final HttpPartParser partParser;
098   private final HttpPartSchema schema;
099   private final boolean multi;
100   private final String name, def;
101   private final ClassInfo type;
102
103   /**
104    * Static creator.
105    *
106    * @param paramInfo The Java method parameter being resolved.
107    * @param annotations The annotations to apply to any new part parsers.
108    * @return A new {@link HeaderArg}, or <jk>null</jk> if the parameter is not annotated with {@link Header}.
109    */
110   public static HeaderArg create(ParamInfo paramInfo, AnnotationWorkList annotations) {
111      if ((!paramInfo.getParameterType().is(Value.class)) && (paramInfo.hasAnnotation(Header.class) || paramInfo.getParameterType().hasAnnotation(Header.class)))
112         return new HeaderArg(paramInfo, annotations);
113      return null;
114   }
115
116   /**
117    * Constructor.
118    *
119    * @param pi The Java method parameter being resolved.
120    * @param annotations The annotations to apply to any new part parsers.
121    */
122   protected HeaderArg(ParamInfo pi, AnnotationWorkList annotations) {
123      this.name = findName(pi).orElseThrow(() -> new ArgException(pi, "@Header used without name or value"));
124      this.def = findDef(pi).orElse(null);
125      this.type = pi.getParameterType();
126      this.schema = HttpPartSchema.create(Header.class, pi);
127      Class<? extends HttpPartParser> pp = schema.getParser();
128      this.partParser = pp != null ? HttpPartParser.creator().type(pp).apply(annotations).create() : null;
129      this.multi = schema.getCollectionFormat() == HttpPartCollectionFormat.MULTI;
130
131      if (multi && ! type.isCollectionOrArray())
132         throw new ArgException(pi, "Use of multipart flag on @Header parameter that is not an array or Collection");
133   }
134
135   @SuppressWarnings({ "rawtypes", "unchecked" })
136   @Override /* RestOpArg */
137   public Object resolve(RestOpSession opSession) throws Exception {
138      RestRequest req = opSession.getRequest();
139      HttpPartParserSession ps = partParser == null ? req.getPartParserSession() : partParser.getPartSession();
140      RequestHeaders rh = req.getHeaders();
141      BeanSession bs = req.getBeanSession();
142      ClassMeta<?> cm = bs.getClassMeta(type.innerType());
143
144      if (multi) {
145         Collection c = cm.isArray() ? list() : (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new JsonList());
146         rh.stream(name).map(x -> x.parser(ps).schema(schema).as(cm.getElementType()).orElse(null)).forEach(x -> c.add(x));
147         return cm.isArray() ? ArrayUtils.toArray(c, cm.getElementType().getInnerClass()) : c;
148      }
149
150      if (cm.isMapOrBean() && isOneOf(name, "*", "")) {
151         JsonMap m = new JsonMap();
152         rh.forEach(x -> m.put(x.getName(), x.parser(ps).schema(schema == null ? null : schema.getProperty(x.getName())).as(cm.getValueType()).orElse(null)));
153         return req.getBeanSession().convertToType(m, cm);
154      }
155
156      return rh.getLast(name).parser(ps).schema(schema).def(def).as(type.innerType()).orElse(null);
157   }
158}