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.httppart.HttpPartType.*;
018import static org.apache.juneau.annotation.InvalidAnnotationException.*;
019
020import java.util.*;
021
022import org.apache.juneau.*;
023import org.apache.juneau.http.annotation.*;
024import org.apache.juneau.httppart.*;
025import org.apache.juneau.reflect.*;
026
027/**
028 * Represents the metadata gathered from a parameter or class annotated with {@link Request}.
029 */
030public class RequestBeanMeta {
031
032   /**
033    * Create metadata from specified parameter.
034    *
035    * @param mpi The method parameter.
036    * @param ps
037    *    Configuration information used to instantiate part serializers and part parsers.
038    *    <br>Can be <jk>null</jk>.
039    * @return Metadata about the parameter, or <jk>null</jk> if parameter or parameter type not annotated with {@link Request}.
040    */
041   public static RequestBeanMeta create(ParamInfo mpi, PropertyStore ps) {
042      if (! mpi.hasAnnotation(Request.class))
043         return null;
044      return new RequestBeanMeta.Builder(ps).apply(mpi).build();
045   }
046
047   /**
048    * Create metadata from specified class.
049    *
050    * @param c The class annotated with {@link Request}.
051    * @param ps
052    *    Configuration information used to instantiate part serializers and part parsers.
053    *    <br>Can be <jk>null</jk>.
054    * @return Metadata about the class, or <jk>null</jk> if class not annotated with {@link Request}.
055    */
056   public static RequestBeanMeta create(Class<?> c, PropertyStore ps) {
057      ClassInfo ci = ClassInfo.of(c);
058      if (! ci.hasAnnotation(Request.class))
059         return null;
060      return new RequestBeanMeta.Builder(ps).apply(c).build();
061   }
062
063   //-----------------------------------------------------------------------------------------------------------------
064   // Instance
065   //-----------------------------------------------------------------------------------------------------------------
066
067   private final ClassMeta<?> cm;
068   private final Map<String,RequestBeanPropertyMeta> properties;
069   private final HttpPartSerializer serializer;
070   private final HttpPartParser parser;
071
072   RequestBeanMeta(Builder b) {
073      this.cm = b.cm;
074      this.serializer = castOrCreate(HttpPartSerializer.class, b.serializer, true, b.ps);
075      this.parser = castOrCreate(HttpPartParser.class, b.parser, true, b.ps);
076      Map<String,RequestBeanPropertyMeta> properties = new LinkedHashMap<>();
077      for (Map.Entry<String,RequestBeanPropertyMeta.Builder> e : b.properties.entrySet())
078         properties.put(e.getKey(), e.getValue().build(serializer, parser));
079      this.properties = Collections.unmodifiableMap(properties);
080   }
081
082   static class Builder {
083      ClassMeta<?> cm;
084      PropertyStore ps;
085      Class<? extends HttpPartSerializer> serializer;
086      Class<? extends HttpPartParser> parser;
087      Map<String,RequestBeanPropertyMeta.Builder> properties = new LinkedHashMap<>();
088
089      Builder(PropertyStore ps) {
090         this.ps = ps;
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         ClassInfo ci = ClassInfo.of(c);
099         apply(ci.getAnnotation(Request.class));
100         this.cm = BeanContext.DEFAULT.getClassMeta(c);
101         for (MethodInfo m : cm.getInfo().getAllMethods()) {
102
103            if (m.isPublic()) {
104               assertNoInvalidAnnotations(m, ResponseHeader.class, ResponseBody.class, ResponseStatus.class);
105               String n = m.getSimpleName();
106               if (m.hasAnnotation(Body.class)) {
107                  assertNoArgs(m, Body.class);
108                  assertReturnNotVoid(m, Body.class);
109                  properties.put(n, RequestBeanPropertyMeta.create(BODY, Body.class, m));
110               } else if (m.hasAnnotation(Header.class)) {
111                  assertNoArgs(m, Header.class);
112                  assertReturnNotVoid(m, Header.class);
113                  properties.put(n, RequestBeanPropertyMeta.create(HEADER, Header.class, m));
114               } else if (m.hasAnnotation(Query.class)) {
115                  assertNoArgs(m, Query.class);
116                  assertReturnNotVoid(m, Query.class);
117                  properties.put(n, RequestBeanPropertyMeta.create(QUERY, Query.class, m));
118               } else if (m.hasAnnotation(FormData.class)) {
119                  assertNoArgs(m, FormData.class);
120                  assertReturnNotVoid(m, FormData.class);
121                  properties.put(n, RequestBeanPropertyMeta.create(FORMDATA, FormData.class, m));
122               } else if (m.hasAnnotation(Path.class)) {
123                  assertNoArgs(m, Path.class);
124                  assertReturnNotVoid(m, Path.class);
125                  properties.put(n, RequestBeanPropertyMeta.create(PATH, Path.class, m));
126               }
127            }
128         }
129         return this;
130      }
131
132      Builder apply(Request a) {
133         if (a != null) {
134            if (a.partSerializer() != HttpPartSerializer.Null.class)
135               serializer = a.partSerializer();
136            if (a.partParser() != HttpPartParser.Null.class)
137               parser = a.partParser();
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}