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.commons.inject;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021
022import java.lang.reflect.*;
023import java.util.*;
024import java.util.concurrent.*;
025import java.util.function.*;
026
027import org.apache.juneau.commons.reflect.*;
028import org.apache.juneau.commons.settings.*;
029import org.apache.juneau.commons.svl.*;
030
031/**
032 * Internal helper used by the reflection layer to resolve {@code @Value}-annotated injection sites.
033 *
034 * <p>
035 * Splits the {@code @Value} handling into three small pieces so callers (field / parameter / method)
036 * can share the same coercion + error-formatting code without duplicating the lookup logic:
037 *
038 * <ol>
039 *    <li>{@link #findValueExpression(List)} — given an annotation list, return the first {@code @Value}
040 *       expression (or {@code null} if no {@code @Value} is present).
041 *    <li>{@link #checkInjectConflict(List, String)} — throw {@link BeanCreationException} if both
042 *       {@code @Value} and {@code @Inject}/{@code @Autowired} are present on the same site.
043 *    <li>{@link #resolve(String, Class, String)} — pass the expression through {@code VarResolver},
044 *       coerce to the target type via {@link Settings#toType(String, Class)}, and wrap failures in
045 *       {@link BeanCreationException}.
046 * </ol>
047 *
048 * <p>
049 * The resolver intentionally uses {@link VarResolver#DEFAULT}, which already includes
050 * {@code PropertyVar} (the {@code $P{key[,default]}} variable that reads from {@link Settings}).
051 * After the property-variable unification, the {@code ${key}} shortcut routes through that same {@code PropertyVar},
052 * so {@code @Value("${db.url}")} and {@code @Value("$P{db.url}")} are equivalent.
053 */
054public final class ValueResolver {
055
056   private ValueResolver() {}
057
058   /**
059    * Per-expression {@link VarTemplate} cache shared by all {@code @Value} injection sites.
060    * The cache key is the {@code @Value} expression text —
061    * which is bounded by the number of distinct expressions in the application, not by user
062    * input — so the unbounded map cannot grow uncontrollably in practice. Compilation
063    * happens at most once per distinct expression; subsequent {@code @Value} resolutions
064    * read the cached {@link VarTemplate} and skip the tokenizer + var-registry-lookup
065    * pipeline entirely.
066    */
067   private static final ConcurrentMap<String,VarTemplate> TEMPLATE_CACHE = new ConcurrentHashMap<>();
068
069   /**
070    * Returns the compiled {@link VarTemplate} for the given expression, computing and caching
071    * on first use. Intended for use by reflection-layer injection sites
072    * ({@link FieldInfo#inject}, {@link ParameterInfo#resolveValue}) that want to amortize
073    * tokenization across repeated bean constructions.
074    *
075    * @param expression The {@code @Value} expression text. Must not be {@code null}.
076    * @return The cached compiled template (uses {@link VarResolver#DEFAULT}).
077    */
078   public static VarTemplate getCompiledTemplate(String expression) {
079      return TEMPLATE_CACHE.computeIfAbsent(expression, VarResolver.DEFAULT::compile);
080   }
081
082   /**
083    * Clears the internal {@link VarTemplate} cache. Primarily intended for tests that need to
084    * exercise the compile-time path repeatedly with different {@link VarResolver}s. Production
085    * code should not need to call this.
086    */
087   public static void clearTemplateCache() {
088      TEMPLATE_CACHE.clear();
089   }
090
091   /**
092    * Returns the {@code value()} expression carried by the first {@code @Value} annotation in the list,
093    * or {@code null} if no {@code @Value} (Juneau or Spring) is present.
094    *
095    * @param annotations The annotation list (from {@link FieldInfo#getAnnotations()},
096    *    {@link ParameterInfo#getAnnotations()}, etc.). May be {@code null} or empty, in which case {@code null} is returned.
097    * @return The expression, or {@code null} if not a {@code @Value} site.
098    */
099   public static String findValueExpression(List<? extends AnnotationInfo<?>> annotations) {
100      if (isEmpty(annotations))
101         return null;
102      return annotations.stream()
103         .map(JsrSupport::valueExpression)
104         .filter(Objects::nonNull)
105         .findFirst()
106         .orElse(null);
107   }
108
109   /**
110    * Returns {@code true} if any annotation in the list is a {@code @Value} marker.
111    *
112    * @param annotations The annotation list.  May be {@code null} or empty, in which case {@code false} is returned.
113    * @return {@code true} if at least one annotation matches {@link JsrSupport#isValueAnnotation(AnnotationInfo)}.
114    */
115   public static boolean hasValueAnnotation(List<? extends AnnotationInfo<?>> annotations) {
116      if (isEmpty(annotations))
117         return false;
118      return annotations.stream().anyMatch(JsrSupport::isValueAnnotation);
119   }
120
121   /**
122    * Throws {@link BeanCreationException} if an injection site carries both {@code @Value} and an
123    * {@code @Inject}/{@code @Autowired} marker.
124    *
125    * <p>
126    * {@code @Value} resolves strings/primitives; {@code @Inject} resolves beans. Combining them on
127    * the same site is ambiguous — fail fast with a clear message.
128    *
129    * @param annotations The annotation list.  May be {@code null} or empty, in which case this method returns without action.
130    * @param siteDescription A human-readable description of the site (used in the error message).
131    */
132   public static void checkInjectConflict(List<? extends AnnotationInfo<?>> annotations, String siteDescription) {
133      if (isEmpty(annotations))
134         return;
135      var hasValue = annotations.stream().anyMatch(JsrSupport::isValueAnnotation);
136      if (! hasValue)
137         return;
138      var hasInject = annotations.stream().anyMatch(JsrSupport::isInjectAnnotation);
139      if (hasInject)
140         throw new BeanCreationException("@Value and @Inject are mutually exclusive on " + siteDescription);
141   }
142
143   /**
144    * Resolves a {@code @Value} expression against the default {@link VarResolver} and coerces the
145    * result to {@code targetType} via {@link Settings#toType(String, Class)}.
146    *
147    * <p>
148    * Lookup semantics:
149    * <ul>
150    *    <li>If the resolved value is {@code null} and {@code targetType} is a primitive, throws
151    *       {@link BeanCreationException} (primitives cannot be {@code null}).
152    *    <li>If the resolved value is {@code null} and {@code targetType} is a reference type, returns
153    *       {@code null}.
154    *    <li>If coercion via {@link Settings#toType(String, Class)} fails, the underlying exception is
155    *       wrapped in {@link BeanCreationException} with a message that includes the expression, the
156    *       resolved string, and the target type.
157    * </ul>
158    *
159    * @param expression The {@code @Value} expression (typically {@code "${key}"} or {@code "${key:default}"}). May be {@code null}, in which case {@code null} is returned for reference types (and a {@link BeanCreationException} is thrown for primitive target types).
160    * @param targetType The target Java type. May be a primitive. Must not be {@code null}.
161    * @param siteDescription A human-readable description of the site (used in error messages).
162    * @return The coerced value.
163    */
164   public static Object resolve(String expression, Class<?> targetType, String siteDescription) {
165      return resolve(expression, targetType, siteDescription, null);
166   }
167
168   /**
169    * Same as {@link #resolve(String, Class, String)} but consults caller-scoped
170    * {@link PropertySource} beans resolved from the supplied {@link BeanStore} between
171    * the {@link Settings} local/global override stores and the {@link Settings} sources chain.
172    *
173    * <p>
174    * The {@code beanStore} parameter is preserved by-reference; if its
175    * {@code PropertySource}-typed contents change between calls, the next resolution picks up
176    * the current state. Passing {@code null} (or a {@code BeanStore} with no
177    * {@code PropertySource}-typed beans) is byte-for-byte equivalent to the zero-argument
178    * overload &mdash; no extra allocation and no behavior change.
179    *
180    * @param expression The {@code @Value} expression. May be {@code null}, in which case {@code null} is returned for reference types (and a {@link BeanCreationException} is thrown for primitive target types).
181    * @param targetType The target Java type. May be a primitive. Must not be {@code null}.
182    * @param siteDescription A human-readable description of the site (used in error messages).
183    * @param beanStore Caller-scoped {@link BeanStore} consulted for {@link PropertySource} beans.
184    *    May be {@code null}.
185    * @return The coerced value.
186    */
187   public static Object resolve(String expression, Class<?> targetType, String siteDescription, BeanStore beanStore) {
188      if (expression == null)
189         return resolveCoerce(null, targetType, expression, siteDescription);
190      var template = getCompiledTemplate(expression);
191      var session = VarResolver.DEFAULT.createSession();
192      var sources = scopedSources(beanStore);
193      if (sources != null)
194         session.bean(PropertySource[].class, sources);
195      var resolved = template.resolve(session);
196      return resolveCoerce(resolved, targetType, expression, siteDescription);
197   }
198
199   /**
200    * Generic-aware {@code @Value} resolution that honors {@link Supplier}{@code <T>}
201    * field types by returning a re-evaluating, threadsafe Supplier instead of a one-shot
202    * resolved value (field-type autodetect — bare {@code String} resolves once, {@code Supplier<T>}
203    * re-evaluates per {@code .get()}).
204    *
205    * <p>
206    * Behavior by {@code genericTargetType}:
207    * <ul>
208    *    <li>{@code Supplier<String>} → returns a {@link Supplier} that re-evaluates the
209    *       compiled template on every {@link Supplier#get()} call, each time against a
210    *       fresh {@link VarResolverSession}. Safe to share across threads.
211    *    <li>{@code Supplier<T>} for {@code T != String} → returns a {@link Supplier} that, on every
212    *       {@link Supplier#get()} call, re-evaluates the template <i>and</i> coerces the resolved
213    *       string into {@code T} via the same {@code String -> T} converter machinery that plain
214    *       {@code @Value T} uses ({@link Settings#toType(String, Class)}). The coerced value is never
215    *       memoized (late-binding contract); coercion failures surface as a {@link RuntimeException}
216    *       from {@code .get()} naming the {@code @Value} key and target type. {@code T} is taken from
217    *       any type the converter registry can build from a {@code String} (e.g. {@code Integer},
218    *       {@code Long}, {@code Boolean}, {@code Duration}, {@code URI}, {@code Path}, enums).
219    *    <li>Anything else (including raw {@code Supplier} and {@code Optional<Supplier<T>>}) → delegates
220    *       to {@link #resolve(String, Class, String)} with the raw target class (preserves existing
221    *       behavior; the {@code Optional<Supplier<T>>} nesting is intentionally not specially handled).
222    * </ul>
223    *
224    * @param expression The {@code @Value} expression. May be {@code null}.
225    * @param targetType The erased target class (e.g. {@code String.class} or {@code Supplier.class}). Must not be {@code null}.
226    * @param genericTargetType The declared generic type (e.g. {@code Supplier<Integer>}). May be
227    *    {@code null}, in which case Supplier-detection is skipped.
228    * @param siteDescription Human-readable site description for error messages.
229    * @return Either a one-shot coerced value, or a {@code Supplier<T>} factory.
230    */
231   public static Object resolve(String expression, Class<?> targetType, Type genericTargetType, String siteDescription) {
232      return resolve(expression, targetType, genericTargetType, siteDescription, null);
233   }
234
235   /**
236    * Same as {@link #resolve(String, Class, Type, String)} but consults caller-scoped
237    * {@link PropertySource} beans resolved from the supplied {@link BeanStore}.
238    *
239    * <p>
240    * The {@code beanStore} reference is captured (not snapshotted) by the returned
241    * {@code Supplier<T>} when the declared field/parameter type is {@code Supplier<...>},
242    * so re-evaluating reads always see the current state of {@code beanStore.getBeansOfType(PropertySource.class)}.
243    *
244    * @param expression The {@code @Value} expression. May be {@code null}.
245    * @param targetType The erased target class. Must not be {@code null}.
246    * @param genericTargetType The declared generic type. May be {@code null}.
247    * @param siteDescription Human-readable site description for error messages.
248    * @param beanStore Caller-scoped {@link BeanStore} consulted for {@link PropertySource} beans.
249    *    May be {@code null}.
250    * @return Either a one-shot coerced value, or a {@code Supplier<T>} factory.
251    */
252   public static Object resolve(String expression, Class<?> targetType, Type genericTargetType, String siteDescription,
253         BeanStore beanStore) {
254      var elementType = supplierElementType(targetType, genericTargetType);
255      if (elementType != null) {
256         // Build the re-evaluating string Supplier once. This is the same factory the bare
257         // Supplier<String> path has always used.
258         var stringSupplier = buildStringSupplier(expression, beanStore);
259         if (elementType == String.class)
260            return stringSupplier;
261         // Supplier<T> for T != String: coerce per .get() using the same String -> T converter
262         // machinery that plain @Value T uses (resolveCoerce -> Settings.toType). Per the
263         // late-binding contract, the string is re-resolved AND re-coerced on every .get() — the
264         // coerced value is never memoized. Only the converter lookup is cached (Settings.toType
265         // memoizes the discovered String -> T function per type in its toTypeFunctions registry,
266         // so each .get() pays the coercion but not the reflective converter discovery). A coercion
267         // failure surfaces as a BeanCreationException (a RuntimeException) from .get(), naming the
268         // @Value expression and the target type.
269         final Class<?> et = elementType;
270         return (Supplier<Object>) () -> resolveCoerce(stringSupplier.get(), et, expression, siteDescription);
271      }
272      return resolve(expression, targetType, siteDescription, beanStore);
273   }
274
275   /**
276    * Builds the re-evaluating {@code Supplier<String>} used by the {@code @Value Supplier<...>}
277    * autodetect path. Each {@link Supplier#get()} re-resolves the compiled template against a fresh
278    * {@link VarResolverSession}, so live config changes are reflected (the late-binding contract).
279    *
280    * @param expression The {@code @Value} expression. May be {@code null}.
281    * @param beanStore Caller-scoped {@link BeanStore} consulted for {@link PropertySource} beans. May be {@code null}.
282    * @return A threadsafe, re-evaluating {@code Supplier<String>}.
283    */
284   private static Supplier<String> buildStringSupplier(String expression, BeanStore beanStore) {
285      if (expression == null)
286         return () -> null;
287      var template = getCompiledTemplate(expression);
288      // Literal templates fast-path: capture the resolved string once and hand back a
289      // constant Supplier. Saves a per-.get() session allocation when the expression
290      // has no variables (e.g. @Value("literal") Supplier<String>).
291      if (template.isLiteral()) {
292         var literal = template.resolve(VarResolver.DEFAULT.createSession());
293         return () -> literal;
294      }
295      if (beanStore == null)
296         return template.asSupplierWithFreshSessions(VarResolver.DEFAULT);
297      // Capture the BeanStore by-reference so re-evaluating reads see the current state of
298      // its PropertySource-typed beans (matches the "by-reference, not snapshot" contract).
299      final BeanStore scope = beanStore;
300      return () -> {
301         var s = VarResolver.DEFAULT.createSession();
302         var sources = scopedSources(scope);
303         if (sources != null)
304            s.bean(PropertySource[].class, sources);
305         return template.resolve(s);
306      };
307   }
308
309   /**
310    * Returns the caller-scoped {@link PropertySource} array for the supplied {@link BeanStore},
311    * or {@code null} if the store has no {@code PropertySource}-typed beans (matching the
312    * "no behavior change when no scoped sources are present" contract).
313    *
314    * <p>
315    * Walks {@link BeanStore#getBeansOfType(Class) beanStore.getBeansOfType(PropertySource.class)}.
316    * The returned array preserves the iteration order of {@code getBeansOfType} (parent-chain
317    * beans before local beans before overriding-parent beans, then sorted by
318    * {@code @Order/@Primary/@Bean.priority()}).
319    */
320   @SuppressWarnings({
321      "java:S1168" // Null is a meaningful "no scoped sources" signal that callers branch on to preserve the no-allocation / no-behavior-change contract; an empty array would alter behavior.
322   })
323   private static PropertySource[] scopedSources(BeanStore beanStore) {
324      if (beanStore == null)
325         return null;
326      var map = beanStore.getBeansOfType(PropertySource.class);
327      if (map.isEmpty())
328         return null;
329      return map.values().toArray(new PropertySource[0]);
330   }
331
332   /**
333    * Returns the {@code T} element class of a declared {@code Supplier<T>} injection site, or
334    * {@code null} if the declared type is not a coercible {@code Supplier<T>}.
335    *
336    * <p>
337    * Returns {@code null} (i.e. "not a typed Supplier site, fall through to one-shot resolution")
338    * for raw {@code Supplier}, non-{@code Supplier} parameterized types such as
339    * {@code Optional<Supplier<T>>} (whose raw type is {@code Optional}, not {@code Supplier} — the
340    * nesting is intentionally not specially handled), and type arguments that are not a concrete
341    * {@link Class} (wildcards / type variables). {@code Supplier<String>} returns {@code String.class},
342    * which the caller treats as the pre-existing pass-through behavior.
343    */
344   private static Class<?> supplierElementType(Class<?> targetType, Type genericTargetType) {
345      if (targetType != Supplier.class)
346         return null;
347      if (!(genericTargetType instanceof ParameterizedType genericTargetType2))
348         return null;
349      if (genericTargetType2.getRawType() != Supplier.class)
350         return null;
351      var args = genericTargetType2.getActualTypeArguments();
352      if (args.length != 1)
353         return null;
354      return args[0] instanceof Class<?> c ? c : null;
355   }
356
357   private static Object resolveCoerce(String resolved, Class<?> targetType, String expression, String siteDescription) {
358      var effectiveType = box(targetType);
359
360      if (ie(resolved)) {
361         if (targetType.isPrimitive())
362            throw new BeanCreationException("Could not resolve required @Value('" + expression + "') for primitive site " + siteDescription);
363         if (resolved == null)
364            return null;
365         if (effectiveType == String.class)
366            return resolved;
367      }
368
369      // Character has no usable valueOf(String) — Settings.toType can't coerce; handle manually.
370      if (effectiveType == Character.class) {
371         if (resolved.length() == 1)
372            return resolved.charAt(0);
373         throw new BeanCreationException(
374            "Could not coerce @Value('" + expression + "') resolving to '" + resolved + "' to char for " + siteDescription);
375      }
376
377      try {
378         return Settings.get().toType(resolved, effectiveType);
379      } catch (RuntimeException e) {
380         throw new BeanCreationException(
381            "Could not coerce @Value('" + expression + "') resolving to '" + resolved + "' to "
382               + targetType.getName() + " for " + siteDescription,
383            e);
384      }
385   }
386
387   /**
388    * Returns the boxed counterpart of a primitive type, or {@code type} itself if not primitive.
389    *
390    * <p>
391    * {@link Settings#toType(String, Class)} uses reflection for {@code valueOf(String)} / {@code (String)}
392    * constructor lookup, which doesn't work for raw primitives ({@code int.class} etc.). Box them first.
393    *
394    * @param type The (possibly primitive) target type.
395    * @return The boxed equivalent, or {@code type} unchanged.
396    */
397   private static Class<?> box(Class<?> type) {
398      if (! type.isPrimitive())
399         return type;
400      if (type == int.class) return Integer.class;
401      if (type == long.class) return Long.class;
402      if (type == boolean.class) return Boolean.class;
403      if (type == double.class) return Double.class;
404      if (type == float.class) return Float.class;
405      if (type == short.class) return Short.class;
406      if (type == byte.class) return Byte.class;
407      if (type == char.class) return Character.class;
408      return type;  // void or unreachable
409   }
410}