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;
014
015import static org.apache.juneau.common.internal.StringUtils.*;
016import static org.apache.juneau.internal.ClassUtils.*;
017import static org.apache.juneau.internal.CollectionUtils.*;
018
019import java.lang.annotation.*;
020import java.nio.charset.*;
021import java.util.*;
022import java.util.stream.*;
023
024import org.apache.juneau.annotation.*;
025import org.apache.juneau.reflect.*;
026import org.apache.juneau.svl.*;
027
028/**
029 * Class used to add properties to a context builder (e.g. {@link BeanContext.Builder}) from an annotation (e.g. {@link BeanConfig}).
030 *
031 * <p>
032 * Used by {@link Context.Builder#applyAnnotations(Class...)} and {@link Context.Builder#applyAnnotations(java.lang.reflect.Method...)} to apply
033 * annotations to context beans.
034 *
035 * <p>
036 * The following code shows the general design pattern.
037 *
038 * <p class='bjava'>
039 *    <jc>// The annotation applied to classes and methods.</jc>
040 *    <ja>@Target</ja>({METHOD,TYPE})
041 *    <ja>@Retention</ja>(<jsf>RUNTIME</jsf>)
042 *    <ja>@ContextApply</ja>(BeanConfigAnnotationApplier.<jk>class</jk>)
043 *    <jk>public</jk> <jk>@interface </jk>BeanConfig {
044 *
045 *       String sortProperties() <jk>default</jk> <js>""</js>;
046 *
047 *    }
048 *
049 *    <jc>// The applier that applies the annotation to the bean context builder.</jc>
050 *    <jk>public class</jk> BeanConfigAnnotationApplier <jk>extends</jk> AnnotationApplier&lt;<ja>BeanConfig</ja>,BeanContext.Builder&gt; {
051 *
052 *    <jc>// Required constructor. </jc>
053 *       <jk>public</jk> Applier(VarResolverSession <jv>vr</jv>) {
054 *          <jk>super</jk>(BeanConfig.<jk>class</jk>, BeanContext.Builder.<jk>class</jk>, <jv>vr</jv>);
055 *       }
056 *
057 *       <ja>@Override</ja>
058 *       <jk>public void</jk> apply(AnnotationInfo&lt;BeanConfig&gt; <jv>annotationInfo</jv>, BeanContext.Builder <jv>builder</jv>) {
059 *          <ja>BeanConfig</ja> <jv>beanConfig</jv> = <jv>annotationInfo</jv>.getAnnotation();
060 *
061 *          String <jv>sortProperties</jv> = <jv>beanConfig</jv>.sortProperties();
062 *          <jk>if</jk> (! <jv>sortProperties</jv>.isEmpty())
063 *             <jv>builder</jv>.sortProperties(Boolean.<jsm>parseBoolean</jsm>(<jv>sortProperties</jv>));
064 *       }
065 *    }
066 *
067 * <jc>// An annotated class.</jc>
068 *    <ja>@BeanConfig</ja>(sortProperties=<js>"true"</js>)
069 *    <jk>public class</jk> AnnotatedClass {}
070 *
071 * <jc>// Putting it together.</jc>
072 *    <jk>public static void</jk> main(String[] <jv>args</jv>) {
073 *
074 *    <jc>// Create a JSON serializer with sorted properties.</jc>
075 *       Serializer <jv>serializer</jv> = JsonSerializer.<jsm>create</jsm>().applyAnnotations(AnnotatedClass.<jk>class</jk>).build();
076 *    }
077 * </p>
078 *
079 * <h5 class='section'>See Also:</h5><ul>
080 * </ul>
081 *
082 * @param <A> The annotation that this applier reads from.
083 * @param <B> The builder class to apply the annotation to.
084 */
085public abstract class AnnotationApplier<A extends Annotation, B> {
086
087   private final VarResolverSession vr;
088   private final Class<A> ca;
089   private final Class<B> cb;
090
091   /**
092    * Constructor.
093    *
094    * @param annotationClass The annotation class.
095    * @param builderClass The annotation class.
096    * @param vr The string resolver to use for resolving strings.
097    */
098   protected AnnotationApplier(Class<A> annotationClass, Class<B> builderClass, VarResolverSession vr) {
099      this.vr = vr == null ? VarResolver.DEFAULT.createSession() : vr;
100      this.ca = annotationClass;
101      this.cb = builderClass;
102   }
103
104   /**
105    * Apply the specified annotation to the specified property store builder.
106    *
107    * @param annotationInfo The annotation.
108    * @param builder The property store builder.
109    */
110   public abstract void apply(AnnotationInfo<A> annotationInfo, B builder);
111
112   /**
113    * Returns <jk>true</jk> if this apply can be appied to the specified builder.
114    *
115    * @param builder The builder to check.
116    * @return <jk>true</jk> if this apply can be appied to the specified builder.
117    */
118   public boolean canApply(Object builder) {
119      return cb.isInstance(builder);
120   }
121
122   /**
123    * Returns the builder class that this applier applies to.
124    *
125    * @return The builder class that this applier applies to.
126    */
127   public Class<?> getBuilderClass() {
128      return cb;
129   }
130
131   /**
132    * Returns the var resolver session for this apply.
133    *
134    * @return The var resolver session for this apply.
135    */
136   protected VarResolverSession vr() {
137      return vr;
138   }
139
140   /**
141    * Resolves the specified string.
142    *
143    * @param in The string containing variables to resolve.
144    * @return An optional containing the specified string if it exists, or {@link Optional#empty()} if it does not.
145    */
146   protected Optional<String> string(String in) {
147      in = vr.resolve(in);
148      return optional(isEmpty(in) ? null : in);
149   }
150
151   /**
152    * Returns the specified value if it's simple name is not <js>"void"</js>.
153    *
154    * @param <T> The value to return.
155    * @param in The value to return.
156    * @return An optional containing the specified value.
157    */
158   protected <T> Optional<Class<T>> type(Class<T> in) {
159      return optional(in).filter(NOT_VOID);
160   }
161
162   /**
163    * Returns the specified string array as an {@link Optional}.
164    *
165    * <p>
166    * If the array is empty, then returns {@link Optional#empty()}.
167    *
168    * @param in The string array.
169    * @return The array wrapped in an {@link Optional}.
170    */
171   protected Optional<String[]> strings(String[] in) {
172      return optional(in.length == 0 ? null : Arrays.stream(in).map(x -> vr.resolve(x)).filter(x -> isNotEmpty(x)).toArray(String[]::new));
173   }
174
175   /**
176    * Resolves the specified string as a comma-delimited list of strings.
177    *
178    * @param in The CDL string containing variables to resolve.
179    * @return An array with resolved strings.
180    */
181   protected Stream<String> stream(String[] in) {
182      return Arrays.stream(in).map(x -> vr.resolve(x)).filter(x -> isNotEmpty(x));
183   }
184
185   /**
186    * Resolves the specified string as a comma-delimited list of strings.
187    *
188    * @param in The CDL string containing variables to resolve.
189    * @return An array with resolved strings.
190    */
191   protected Stream<String> cdl(String in) {
192      return Arrays.stream(split(vr.resolve(in))).filter(x -> isNotEmpty(x));
193   }
194
195   /**
196    * Resolves the specified string and converts it to a boolean.
197    *
198    * @param in The string containing variables to resolve.
199    * @return The resolved boolean.
200    */
201   public Optional<Boolean> bool(String in) {
202      return string(in).map(Boolean::parseBoolean);
203   }
204
205   /**
206    * Resolves the specified string and converts it to an int.
207    *
208    * @param in The string containing variables to resolve.
209    * @param loc The annotation field name.
210    * @return The resolved int.
211    */
212   protected Optional<Integer> integer(String in, String loc) {
213      try {
214         return string(in).map(Integer::parseInt);
215      } catch (NumberFormatException e) {
216         throw new ConfigException("Invalid syntax for integer on annotation @{0}({1}): {2}", ca.getSimpleName(), loc, in);
217      }
218   }
219
220   /**
221    * Resolves the specified string and converts it to a Charset.
222    *
223    * @param in The string containing variables to resolve.
224    * @return The resolved Charset.
225    */
226   protected Optional<Charset> charset(String in) {
227      return string(in).map(x -> "default".equalsIgnoreCase(x) ? Charset.defaultCharset() : Charset.forName(x));
228   }
229
230   /**
231    * Resolves the specified string and converts it to a Character.
232    *
233    * @param in The string containing variables to resolve.
234    * @param loc The annotation field name.
235    * @return The resolved Character.
236    */
237   protected Optional<Character> character(String in, String loc) {
238      return string(in).map(x -> toCharacter(x, loc));
239   }
240
241   private Character toCharacter(String in, String loc) {
242      if (in.length() != 1)
243         throw new ConfigException("Invalid syntax for character on annotation @{0}({1}): {2}", ca.getSimpleName(), loc, in);
244      return in.charAt(0);
245   }
246
247   /**
248    * Returns the specified class array as an {@link Optional}.
249    *
250    * <p>
251    * If the array is empty, then returns {@link Optional#empty()}.
252    *
253    * @param in The class array.
254    * @return The array wrapped in an {@link Optional}.
255    */
256   protected Optional<Class<?>[]> classes(Class<?>[] in) {
257      return optional(in.length == 0 ? null : in);
258   }
259
260   /**
261    * Represents a no-op configuration apply.
262    */
263   public static class NoOp extends AnnotationApplier<Annotation,Object> {
264
265      /**
266       * Constructor.
267       *
268       * @param r The string resolver to use for resolving strings.
269       */
270      public NoOp(VarResolverSession r) {
271         super(Annotation.class, Object.class, r);
272      }
273
274      @Override /* ConfigApply */
275      public void apply(AnnotationInfo<Annotation> ai, Object b) {}
276   }
277}