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