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