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