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.http.annotation; 018 019import static java.lang.annotation.ElementType.*; 020import static java.lang.annotation.RetentionPolicy.*; 021import static org.apache.juneau.Constants.*; 022import static org.apache.juneau.commons.utils.CollectionUtils.*; 023import static org.apache.juneau.commons.utils.StringUtils.*; 024import static org.apache.juneau.commons.utils.Utils.*; 025 026import java.lang.annotation.*; 027import java.lang.reflect.*; 028import java.util.*; 029 030import org.apache.juneau.*; 031import org.apache.juneau.annotation.*; 032import org.apache.juneau.httppart.*; 033import org.apache.juneau.commons.annotation.*; 034import org.apache.juneau.commons.reflect.*; 035import org.apache.juneau.svl.*; 036 037/** 038 * Utility classes and methods for the {@link Path @Path} annotation. 039 * 040 */ 041public class PathAnnotation { 042 043 private static final AnnotationProvider AP = AnnotationProvider.INSTANCE; 044 045 /** 046 * Applies targeted {@link Path} annotations to a {@link org.apache.juneau.BeanContext.Builder}. 047 */ 048 public static class Applier extends AnnotationApplier<Path,BeanContext.Builder> { 049 050 /** 051 * Constructor. 052 * 053 * @param vr The resolver for resolving values in annotations. 054 */ 055 public Applier(VarResolverSession vr) { 056 super(Path.class, BeanContext.Builder.class, vr); 057 } 058 059 @Override 060 public void apply(AnnotationInfo<Path> ai, BeanContext.Builder b) { 061 Path a = ai.inner(); 062 if (isEmptyArray(a.on()) && isEmptyArray(a.onClass())) 063 return; 064 b.annotations(a); 065 } 066 } 067 068 /** 069 * A collection of {@link Path @Path annotations}. 070 */ 071 @Documented 072 @Target({ METHOD, TYPE }) 073 @Retention(RUNTIME) 074 @Inherited 075 public static @interface Array { 076 077 /** 078 * The child annotations. 079 * 080 * @return The annotation value. 081 */ 082 Path[] value(); 083 } 084 085 /** 086 * Builder class. 087 * 088 * <h5 class='section'>See Also:</h5><ul> 089 * <li class='jm'>{@link org.apache.juneau.BeanContext.Builder#annotations(Annotation...)} 090 * </ul> 091 */ 092 public static class Builder extends AppliedAnnotationObject.BuilderTMF { 093 094 private String[] description = {}; 095 private Class<? extends HttpPartParser> parser = HttpPartParser.Void.class; 096 private Class<? extends HttpPartSerializer> serializer = HttpPartSerializer.Void.class; 097 private Schema schema = SchemaAnnotation.DEFAULT; 098 private String name = "", value = "", def = NONE; 099 100 /** 101 * Constructor. 102 */ 103 protected Builder() { 104 super(Path.class); 105 } 106 107 /** 108 * Instantiates a new {@link Path @Path} object initialized with this builder. 109 * 110 * @return A new {@link Path @Path} object. 111 */ 112 public Path build() { 113 return new Object(this); 114 } 115 116 /** 117 * Sets the description property on this annotation. 118 * 119 * @param value The new value for this property. 120 * @return This object. 121 */ 122 public Builder description(String...value) { 123 description = value; 124 return this; 125 } 126 127 /** 128 * Sets the {@link Path#name} property on this annotation. 129 * 130 * @param value The new value for this property. 131 * @return This object. 132 */ 133 public Builder def(String value) { 134 def = value; 135 return this; 136 } 137 138 /** 139 * Sets the {@link Path#name} property on this annotation. 140 * 141 * @param value The new value for this property. 142 * @return This object. 143 */ 144 public Builder name(String value) { 145 name = value; 146 return this; 147 } 148 149 /** 150 * Sets the {@link Path#parser} property on this annotation. 151 * 152 * @param value The new value for this property. 153 * @return This object. 154 */ 155 public Builder parser(Class<? extends HttpPartParser> value) { 156 parser = value; 157 return this; 158 } 159 160 /** 161 * Sets the {@link Path#schema} property on this annotation. 162 * 163 * @param value The new value for this property. 164 * @return This object. 165 */ 166 public Builder schema(Schema value) { 167 schema = value; 168 return this; 169 } 170 171 /** 172 * Sets the {@link Path#serializer} property on this annotation. 173 * 174 * @param value The new value for this property. 175 * @return This object. 176 */ 177 public Builder serializer(Class<? extends HttpPartSerializer> value) { 178 serializer = value; 179 return this; 180 } 181 182 /** 183 * Sets the {@link Path#value} property on this annotation. 184 * 185 * @param value The new value for this property. 186 * @return This object. 187 */ 188 public Builder value(String value) { 189 this.value = value; 190 return this; 191 } 192 193 @Override /* Overridden from AppliedAnnotationObject.Builder */ 194 public Builder on(String...value) { 195 super.on(value); 196 return this; 197 } 198 199 @Override /* Overridden from AppliedAnnotationObject.BuilderT */ 200 public Builder on(Class<?>...value) { 201 super.on(value); 202 return this; 203 } 204 205 @Override /* Overridden from AppliedOnClassAnnotationObject.Builder */ 206 public Builder onClass(Class<?>...value) { 207 super.onClass(value); 208 return this; 209 } 210 211 @Override /* Overridden from AppliedAnnotationObject.BuilderM */ 212 public Builder on(Method...value) { 213 super.on(value); 214 return this; 215 } 216 217 @Override /* Overridden from AppliedAnnotationObject.BuilderMF */ 218 public Builder on(Field...value) { 219 super.on(value); 220 return this; 221 } 222 223 @Override /* Overridden from AppliedAnnotationObject.BuilderT */ 224 public Builder on(ClassInfo...value) { 225 super.on(value); 226 return this; 227 } 228 229 @Override /* Overridden from AppliedAnnotationObject.BuilderT */ 230 public Builder onClass(ClassInfo...value) { 231 super.onClass(value); 232 return this; 233 } 234 235 @Override /* Overridden from AppliedAnnotationObject.BuilderTMF */ 236 public Builder on(FieldInfo...value) { 237 super.on(value); 238 return this; 239 } 240 241 @Override /* Overridden from AppliedAnnotationObject.BuilderTMF */ 242 public Builder on(MethodInfo...value) { 243 super.on(value); 244 return this; 245 } 246 247 } 248 249 private static class Object extends AppliedOnClassAnnotationObject implements Path { 250 251 private final String[] description; 252 private final Class<? extends HttpPartParser> parser; 253 private final Class<? extends HttpPartSerializer> serializer; 254 private final String name, value, def; 255 private final Schema schema; 256 257 Object(PathAnnotation.Builder b) { 258 super(b); 259 description = copyOf(b.description); 260 def = b.def; 261 name = b.name; 262 parser = b.parser; 263 schema = b.schema; 264 serializer = b.serializer; 265 value = b.value; 266 } 267 268 @Override /* Overridden from Path */ 269 public String def() { 270 return def; 271 } 272 273 @Override /* Overridden from Path */ 274 public String name() { 275 return name; 276 } 277 278 @Override /* Overridden from Path */ 279 public Class<? extends HttpPartParser> parser() { 280 return parser; 281 } 282 283 @Override /* Overridden from Path */ 284 public Schema schema() { 285 return schema; 286 } 287 288 @Override /* Overridden from Path */ 289 public Class<? extends HttpPartSerializer> serializer() { 290 return serializer; 291 } 292 293 @Override /* Overridden from Path */ 294 public String value() { 295 return value; 296 } 297 298 @Override /* Overridden from annotation */ 299 public String[] description() { 300 return description; 301 } 302 } 303 304 /** Default value */ 305 public static final Path DEFAULT = create().build(); 306 307 /** 308 * Instantiates a new builder for this class. 309 * 310 * @return A new builder object. 311 */ 312 public static Builder create() { 313 return new Builder(); 314 } 315 316 /** 317 * Instantiates a new builder for this class. 318 * 319 * @param on The targets this annotation applies to. 320 * @return A new builder object. 321 */ 322 public static Builder create(Class<?>...on) { 323 return create().on(on); 324 } 325 326 /** 327 * Instantiates a new builder for this class. 328 * 329 * @param on The targets this annotation applies to. 330 * @return A new builder object. 331 */ 332 public static Builder create(String...on) { 333 return create().on(on); 334 } 335 336 /** 337 * Returns <jk>true</jk> if the specified annotation contains all default values. 338 * 339 * @param a The annotation to check. 340 * @return <jk>true</jk> if the specified annotation contains all default values. 341 */ 342 public static boolean empty(Path a) { 343 return a == null || DEFAULT.equals(a); 344 } 345 346 /** 347 * Finds the default value from the specified list of annotations. 348 * 349 * @param pi The parameter. 350 * @return The last matching default value, or empty if not found. 351 */ 352 public static Optional<String> findDef(ParameterInfo pi) { 353 // @formatter:off 354 return AP.find(Header.class, pi) 355 .stream() 356 .map(AnnotationInfo::inner) 357 .filter(x -> ne(x.def()) && neq(NONE, x.def())) 358 .findFirst() 359 .map(x -> x.def()); 360 // @formatter:on 361 } 362 363 /** 364 * Finds the name from the specified lists of annotations. 365 * 366 * <p> 367 * The last matching name found is returned. 368 * 369 * @param pi The parameter. 370 * @return The last matching name, or empty if not found. 371 */ 372 public static Optional<String> findName(ParameterInfo pi) { 373 return AP.find(Path.class, pi).stream().map(AnnotationInfo::inner).filter(x -> isAnyNotBlank(x.value(), x.name())).findFirst().map(x -> firstNonBlank(x.name(), x.value())); 374 } 375}