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.httppart.bean;
014
015import static org.apache.juneau.httppart.bean.Utils.*;
016import static org.apache.juneau.internal.ClassUtils.*;
017import static org.apache.juneau.internal.CollectionUtils.*;
018import static org.apache.juneau.httppart.HttpPartType.*;
019
020import java.util.*;
021
022import org.apache.juneau.*;
023import org.apache.juneau.cp.*;
024import org.apache.juneau.http.annotation.*;
025import org.apache.juneau.httppart.*;
026import org.apache.juneau.reflect.*;
027
028/**
029 * Represents the metadata gathered from a parameter or class annotated with {@link Request}.
030 *
031 * <h5 class='section'>See Also:</h5><ul>
032 *    <li class='link'><a class="doclink" href="../../../../../index.html#jm.HttpPartSerializersParsers">HTTP Part Serializers and Parsers</a>
033 * </ul>
034 */
035public class RequestBeanMeta {
036
037   /**
038    * Create metadata from specified parameter.
039    *
040    * @param mpi The method parameter.
041    * @param annotations The annotations to apply to any new part serializers or parsers.
042    * @return Metadata about the parameter, or <jk>null</jk> if parameter or parameter type not annotated with {@link Request}.
043    */
044   public static RequestBeanMeta create(ParamInfo mpi, AnnotationWorkList annotations) {
045      if (mpi.hasNoAnnotation(Request.class))
046         return null;
047      return new RequestBeanMeta.Builder(annotations).apply(mpi).build();
048   }
049
050   /**
051    * Create metadata from specified class.
052    *
053    * @param c The class annotated with {@link Request}.
054    * @param annotations The annotations to apply to any new part serializers or parsers.
055    * @return Metadata about the class, or <jk>null</jk> if class not annotated with {@link Request}.
056    */
057   public static RequestBeanMeta create(Class<?> c, AnnotationWorkList annotations) {
058      ClassInfo ci = ClassInfo.of(c);
059      if (ci.hasNoAnnotation(Request.class))
060         return null;
061      return new RequestBeanMeta.Builder(annotations).apply(c).build();
062   }
063
064   //-----------------------------------------------------------------------------------------------------------------
065   // Instance
066   //-----------------------------------------------------------------------------------------------------------------
067
068   private final ClassMeta<?> cm;
069   private final Map<String,RequestBeanPropertyMeta> properties;
070   private final HttpPartSerializer serializer;
071   private final HttpPartParser parser;
072
073   RequestBeanMeta(Builder b) {
074      this.cm = b.cm;
075      this.serializer = b.serializer.orElse(null);
076      this.parser = b.parser.orElse(null);
077      Map<String,RequestBeanPropertyMeta> properties = map();
078      b.properties.forEach((k,v) -> properties.put(k, v.build(serializer, parser)));
079      this.properties = unmodifiable(properties);
080   }
081
082   static class Builder {
083      ClassMeta<?> cm;
084      AnnotationWorkList annotations;
085      BeanCreator<HttpPartSerializer> serializer = BeanCreator.of(HttpPartSerializer.class);
086      BeanCreator<HttpPartParser> parser = BeanCreator.of(HttpPartParser.class);
087      Map<String,RequestBeanPropertyMeta.Builder> properties = map();
088
089      Builder(AnnotationWorkList annotations) {
090         this.annotations = annotations;
091      }
092
093      Builder apply(ParamInfo mpi) {
094         return apply(mpi.getParameterType().inner()).apply(mpi.getAnnotation(Request.class));
095      }
096
097      Builder apply(Class<?> c) {
098         this.cm = BeanContext.DEFAULT.getClassMeta(c);
099         apply(cm.getLastAnnotation(Request.class));
100         cm.getInfo().forEachPublicMethod(x -> true, x -> {
101            String n = x.getSimpleName();
102            if (x.hasAnnotation(Header.class)) {
103               assertNoArgs(x, Header.class);
104               assertReturnNotVoid(x, Header.class);
105               properties.put(n, RequestBeanPropertyMeta.create(HEADER, Header.class, x));
106            } else if (x.hasAnnotation(Query.class)) {
107               assertNoArgs(x, Query.class);
108               assertReturnNotVoid(x, Query.class);
109               properties.put(n, RequestBeanPropertyMeta.create(QUERY, Query.class, x));
110            } else if (x.hasAnnotation(FormData.class)) {
111               assertNoArgs(x, FormData.class);
112               assertReturnNotVoid(x, FormData.class);
113               properties.put(n, RequestBeanPropertyMeta.create(FORMDATA, FormData.class, x));
114            } else if (x.hasAnnotation(Path.class)) {
115               assertNoArgs(x, Path.class);
116               assertReturnNotVoid(x, Path.class);
117               properties.put(n, RequestBeanPropertyMeta.create(PATH, Path.class, x));
118            } else if (x.hasAnnotation(Content.class)) {
119               assertNoArgs(x, Content.class);
120               assertReturnNotVoid(x, Content.class);
121               properties.put(n, RequestBeanPropertyMeta.create(BODY, Content.class, x));
122            }
123         });
124         return this;
125      }
126
127      Builder apply(Request a) {
128         if (a != null) {
129            if (isNotVoid(a.serializer()))
130               serializer.type(a.serializer());
131            if (isNotVoid(a.parser()))
132               parser.type(a.parser());
133         }
134         return this;
135      }
136
137      RequestBeanMeta build() {
138         return new RequestBeanMeta(this);
139      }
140   }
141
142   /**
143    * Returns metadata about the class.
144    *
145    * @return Metadata about the class.
146    */
147   public ClassMeta<?> getClassMeta() {
148      return cm;
149   }
150
151   /**
152    * Returns metadata about the bean property with the specified property name.
153    *
154    * @param name The bean property name.
155    * @return Metadata about the bean property, or <jk>null</jk> if none found.
156    */
157   public RequestBeanPropertyMeta getProperty(String name) {
158      return properties.get(name);
159   }
160
161   /**
162    * Returns all the annotated methods on this bean.
163    *
164    * @return All the annotated methods on this bean.
165    */
166   public Collection<RequestBeanPropertyMeta> getProperties() {
167      return properties.values();
168   }
169}