    001// Copyright 2006-2025 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// 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
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.ioc.internal.util;
    016
    017import org.apache.tapestry5.commons.*;
    018import org.apache.tapestry5.commons.internal.util.InternalCommonsUtils;
    019import org.apache.tapestry5.commons.services.Coercion;
    020import org.apache.tapestry5.commons.services.PlasticProxyFactory;
    021import org.apache.tapestry5.commons.util.CollectionFactory;
    022import org.apache.tapestry5.commons.util.CommonsUtils;
    023import org.apache.tapestry5.commons.util.ExceptionUtils;
    024import org.apache.tapestry5.func.F;
    025import org.apache.tapestry5.func.Mapper;
    026import org.apache.tapestry5.func.Predicate;
    027import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
    028import org.apache.tapestry5.ioc.AdvisorDef;
    029import org.apache.tapestry5.ioc.AdvisorDef2;
    030import org.apache.tapestry5.ioc.IOCConstants;
    031import org.apache.tapestry5.ioc.Invokable;
    032import org.apache.tapestry5.ioc.ModuleBuilderSource;
    033import org.apache.tapestry5.ioc.OperationTracker;
    034import org.apache.tapestry5.ioc.ServiceAdvisor;
    035import org.apache.tapestry5.ioc.ServiceBuilderResources;
    036import org.apache.tapestry5.ioc.ServiceDecorator;
    037import org.apache.tapestry5.ioc.ServiceLifecycle;
    038import org.apache.tapestry5.ioc.ServiceLifecycle2;
    039import org.apache.tapestry5.ioc.ServiceResources;
    040import org.apache.tapestry5.ioc.annotations.*;
    041import org.apache.tapestry5.ioc.def.*;
    042import org.apache.tapestry5.ioc.internal.ServiceDefImpl;
    043import org.apache.tapestry5.plastic.PlasticUtils;
    044import org.slf4j.Logger;
    045
    046import javax.annotation.PostConstruct;
    047import javax.inject.Named;
    048
    049import java.io.Closeable;
    050import java.io.IOException;
    051import java.lang.annotation.Annotation;
    052import java.lang.annotation.Retention;
    053import java.lang.annotation.RetentionPolicy;
    054import java.lang.reflect.*;
    055import java.net.URL;
    056import java.util.*;
    057import java.util.concurrent.atomic.AtomicLong;
    058import java.util.regex.Matcher;
    059import java.util.regex.Pattern;
    060
    061/**
    062 * Utilities used within various internal implementations of the tapestry-ioc module.
    063 */
    064@SuppressWarnings("all")
    065public class InternalUtils
    066{
    067    /**
    068     * @since 5.2.2
    069     */
    070    public static final boolean SERVICE_CLASS_RELOADING_ENABLED = Boolean.parseBoolean(System.getProperty(
    071            IOCConstants.SERVICE_CLASS_RELOADING_ENABLED, "true"));
    072
    073    /**
    074     * Converts a method to a user presentable string using a {@link PlasticProxyFactory} to obtain a {@link Location}
    075     * (where possible). {@link #asString(Method)} is used under the covers, to present a detailed, but not excessive,
    076     * description of the class, method and parameters.
    077     *
    078     * @param method
    079     *         method to convert to a string
    080     * @param proxyFactory
    081     *         used to obtain the {@link Location}
    082     * @return the method formatted for presentation to the user
    083     */
    084    public static String asString(Method method, PlasticProxyFactory proxyFactory)
    085    {
    086        Location location = proxyFactory.getMethodLocation(method);
    087
    088        return location != null ? location.toString() : asString(method);
    089    }
    090
    091    /**
    092     * Converts a method to a user presentable string consisting of the containing class name, the method name, and the
    093     * short form of the parameter list (the class name of each parameter type, shorn of the package name portion).
    094     *
    095     * @param method
    096     * @return short string representation
    097     */
    098    public static String asString(Method method)
    099    {
    100        return InternalCommonsUtils.asString(method);
    101    }
    102
    103    /**
    104     * Returns the size of an object array, or null if the array is empty.
    105     */
    106
    107    public static int size(Object[] array)
    108    {
    109        return array == null ? 0 : array.length;
    110    }
    111
    112    public static int size(Collection collection)
    113    {
    114        return collection == null ? 0 : collection.size();
    115    }
    116
    117    /**
    118     * Strips leading "_" and "$" and trailing "_" from the name.
    119     */
    120    public static String stripMemberName(String memberName)
    121    {
    122        return InternalCommonsUtils.stripMemberName(memberName);
    123    }
    124
    125    /**
    126     * Converts an enumeration (of Strings) into a sorted list of Strings.
    127     */
    128    public static List<String> toList(Enumeration e)
    129    {
    130        List<String> result = CollectionFactory.newList();
    131
    132        while (e.hasMoreElements())
    133        {
    134            String name = (String) e.nextElement();
    135
    136            result.add(name);
    137        }
    138
    139        Collections.sort(result);
    140
    141        return result;
    142    }
    143
    144    /**
    145     * Finds a specific annotation type within an array of annotations.
    146     *
    147     * @param <T>
    148     * @param annotations
    149     *         to search
    150     * @param annotationClass
    151     *         to match
    152     * @return the annotation instance, if found, or null otherwise
    153     */
    154    public static <T extends Annotation> T findAnnotation(Annotation[] annotations, Class<T> annotationClass)
    155    {
    156        for (Annotation a : annotations)
    157        {
    158            if (annotationClass.isInstance(a))
    159                return annotationClass.cast(a);
    160        }
    161
    162        return null;
    163    }
    164
    165    private static ObjectCreator<Object> asObjectCreator(final Object fixedValue)
    166    {
    167        return new ObjectCreator<Object>()
    168        {
    169            @Override
    170            public Object createObject()
    171            {
    172                return fixedValue;
    173            }
    174        };
    175    }
    176
    177    private static ObjectCreator calculateInjection(final Class injectionType, Type genericType, final Annotation[] annotations,
    178                                                    final ObjectLocator locator, InjectionResources resources)
    179    {
    180        final AnnotationProvider provider = new AnnotationProvider()
    181        {
    182            @Override
    183            public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    184            {
    185                return findAnnotation(annotations, annotationClass);
    186            }
    187        };
    188
    189        // At some point, it would be nice to eliminate InjectService, and rely
    190        // entirely on service interface type and point-of-injection markers.
    191
    192        InjectService is = provider.getAnnotation(InjectService.class);
    193
    194        if (is != null)
    195        {
    196            String serviceId = is.value();
    197
    198            return asObjectCreator(locator.getService(serviceId, injectionType));
    199        }
    200
    201        Named named = provider.getAnnotation(Named.class);
    202
    203        if (named != null)
    204        {
    205            return asObjectCreator(locator.getService(named.value(), injectionType));
    206        }
    207
    208
    209        // TAP5-2758: An @Symbol is treated as an explicit signal that the injected value
    210        // should not be autowired, but resolved using the MasterObjectProvider instead.
    211        // The check needs to be done before the @Inject one, or the value is solely treated
    212        // by its type.
    213        // Without this check, if any configuration is used, no List/Collection/Map value as
    214        // Symbol can be used without both @Inject and @Symbol, as @Symbol has no explicit
    215        // meaning on how to inject the value, and the configuration detector strikes before
    216        // the final fallback to the MasterObjectProvider.
    217
    218        Symbol symbol = provider.getAnnotation(Symbol.class);
    219
    220        if (symbol != null)
    221        {
    222            return asObjectCreator(locator.getObject(injectionType, provider));
    223        }
    224
    225        // In the absence of @InjectService, try some autowiring. First, does the
    226        // parameter type match one of the resources (the parameter defaults)?
    227
    228        if (provider.getAnnotation(Inject.class) == null)
    229        {
    230            Object result = resources.findResource(injectionType, genericType);
    231
    232            if (result != null)
    233            {
    234                return asObjectCreator(result);
    235            }
    236        }
    237
    238        // TAP5-1765: For @Autobuild, special case where we always compute a fresh value
    239        // for the injection on every use.  Elsewhere, we compute once when generating the
    240        // construction plan and just use the singleton value repeatedly.
    241
    242        if (provider.getAnnotation(Autobuild.class) != null)
    243        {
    244            return new ObjectCreator()
    245            {
    246                @Override
    247                public Object createObject()
    248                {
    249                    return locator.getObject(injectionType, provider);
    250                }
    251            };
    252        }
    253
    254        // Otherwise, make use of the MasterObjectProvider service to resolve this type (plus
    255        // any other information gleaned from additional annotation) into the correct object.
    256
    257        return asObjectCreator(locator.getObject(injectionType, provider));
    258    }
    259
    260    public static ObjectCreator[] calculateParametersForMethod(Method method, ObjectLocator locator,
    261                                                               InjectionResources resources, OperationTracker tracker)
    262    {
    263
    264        return calculateParameters(locator, resources, method.getParameterTypes(), method.getGenericParameterTypes(),
    265                method.getParameterAnnotations(), tracker);
    266    }
    267
    268    public static ObjectCreator[] calculateParameters(final ObjectLocator locator, final InjectionResources resources,
    269                                                      Class[] parameterTypes, final Type[] genericTypes, Annotation[][] parameterAnnotations,
    270                                                      OperationTracker tracker)
    271    {
    272        int parameterCount = parameterTypes.length;
    273
    274        ObjectCreator[] parameters = new ObjectCreator[parameterCount];
    275
    276        for (int i = 0; i < parameterCount; i++)
    277        {
    278            final Class type = parameterTypes[i];
    279            final Type genericType = genericTypes[i];
    280            final Annotation[] annotations = parameterAnnotations[i];
    281
    282            String description = String.format("Determining injection value for parameter #%d (%s)", i + 1,
    283                    PlasticUtils.toTypeName(type));
    284
    285            final Invokable<ObjectCreator> operation = new Invokable<ObjectCreator>()
    286            {
    287                @Override
    288                public ObjectCreator invoke()
    289                {
    290                    return calculateInjection(type, genericType, annotations, locator, resources);
    291                }
    292            };
    293
    294            parameters[i] = tracker.invoke(description, operation);
    295        }
    296
    297        return parameters;
    298    }
    299
    300    /**
    301     * Injects into the fields (of all visibilities) when the {@link org.apache.tapestry5.ioc.annotations.Inject} or
    302     * {@link org.apache.tapestry5.ioc.annotations.InjectService} annotations are present.
    303     *
    304     * @param object
    305     *         to be initialized
    306     * @param locator
    307     *         used to resolve external dependencies
    308     * @param resources
    309     *         provides injection resources for fields
    310     * @param tracker
    311     *         track operations
    312     */
    313    public static void injectIntoFields(final Object object, final ObjectLocator locator,
    314                                        final InjectionResources resources, OperationTracker tracker)
    315    {
    316        Class clazz = object.getClass();
    317
    318        while (clazz != Object.class)
    319        {
    320            Field[] fields = clazz.getDeclaredFields();
    321
    322            for (final Field f : fields)
    323            {
    324                // Ignore all static and final fields.
    325
    326                int fieldModifiers = f.getModifiers();
    327
    328                if (Modifier.isStatic(fieldModifiers) || Modifier.isFinal(fieldModifiers))
    329                    continue;
    330
    331                final AnnotationProvider ap = new AnnotationProvider()
    332                {
    333                    @Override
    334                    public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    335                    {
    336                        return f.getAnnotation(annotationClass);
    337                    }
    338                };
    339
    340                String description = String.format("Calculating possible injection value for field %s.%s (%s)",
    341                        clazz.getName(), f.getName(),
    342                        PlasticUtils.toTypeName(f.getType()));
    343
    344                tracker.run(description, new Runnable()
    345                {
    346                    @Override
    347                    public void run()
    348                    {
    349                        final Class<?> fieldType = f.getType();
    350
    351                        InjectService is = ap.getAnnotation(InjectService.class);
    352                        if (is != null)
    353                        {
    354                            inject(object, f, locator.getService(is.value(), fieldType));
    355                            return;
    356                        }
    357
    358                        if (ap.getAnnotation(Inject.class) != null || ap.getAnnotation(InjectResource.class) != null)
    359                        {
    360                            Object value = resources.findResource(fieldType, f.getGenericType());
    361
    362                            if (value != null)
    363                            {
    364                                inject(object, f, value);
    365                                return;
    366                            }
    367
    368                            inject(object, f, locator.getObject(fieldType, ap));
    369                            return;
    370                        }
    371
    372                        if (ap.getAnnotation(javax.inject.Inject.class) != null)
    373                        {
    374                            Named named = ap.getAnnotation(Named.class);
    375
    376                            if (named == null)
    377                            {
    378                                Object value = resources.findResource(fieldType, f.getGenericType());
    379
    380                                if (value != null)
    381                                {
    382                                    inject(object, f, value);
    383                                    return;
    384                                }
    385
    386                                inject(object, f, locator.getObject(fieldType, ap));
    387                            } else
    388                            {
    389                                inject(object, f, locator.getService(named.value(), fieldType));
    390                            }
    391
    392                            return;
    393                        }
    394
    395                        // Ignore fields that do not have the necessary annotation.
    396
    397                    }
    398                });
    399            }
    400
    401            clazz = clazz.getSuperclass();
    402        }
    403    }
    404
    405    private synchronized static void inject(Object target, Field field, Object value)
    406    {
    407        try
    408        {
    409            if (!field.isAccessible())
    410                field.setAccessible(true);
    411
    412            field.set(target, value);
    413
    414            // Is there a need to setAccessible back to false?
    415        } catch (Exception ex)
    416        {
    417            throw new RuntimeException(String.format("Unable to set field '%s' of %s to %s: %s", field.getName(),
    418                    target, value, ExceptionUtils.toMessage(ex)));
    419        }
    420    }
    421
    422    /**
    423     * Joins together some number of elements to form a comma separated list.
    424     */
    425    public static String join(List elements)
    426    {
    427        return InternalCommonsUtils.join(elements);
    428    }
    429
    430    /**
    431     * Joins together some number of elements. If a value in the list is the empty string, it is replaced with the
    432     * string "(blank)".
    433     *
    434     * @param elements
    435     *         objects to be joined together
    436     * @param separator
    437     *         used between elements when joining
    438     */
    439    public static String join(List elements, String separator)
    440    {
    441        return InternalCommonsUtils.join(elements, separator);
    442    }
    443
    444    /**
    445     * Creates a sorted copy of the provided elements, then turns that into a comma separated list.
    446     *
    447     * @return the elements converted to strings, sorted, joined with comma ... or "(none)" if the elements are null or
    448     *         empty
    449     */
    450    public static String joinSorted(Collection elements)
    451    {
    452        return InternalCommonsUtils.joinSorted(elements);
    453    }
    454
    455    /**
    456     * Returns true if the input is null, or is a zero length string (excluding leading/trailing whitespace).
    457     */
    458
    459    public static boolean isBlank(String input)
    460    {
    461        return CommonsUtils.isBlank(input);
    462    }
    463
    464    /**
    465     * Returns true if the input is an empty collection.
    466     */
    467
    468    public static boolean isEmptyCollection(Object input)
    469    {
    470        if (input instanceof Collection)
    471        {
    472            return ((Collection) input).isEmpty();
    473        }
    474
    475        return false;
    476    }
    477
    478    public static boolean isNonBlank(String input)
    479    {
    480        return InternalCommonsUtils.isNonBlank(input);
    481    }
    482
    483    /**
    484     * Capitalizes a string, converting the first character to uppercase.
    485     */
    486    public static String capitalize(String input)
    487    {
    488        return InternalCommonsUtils.capitalize(input);
    489    }
    490
    491    /**
    492     * Sniffs the object to see if it is a {@link Location} or {@link Locatable}. Returns null if null or not
    493     * convertable to a location.
    494     */
    495
    496    public static Location locationOf(Object location)
    497    {
    498        return InternalCommonsUtils.locationOf(location);
    499    }
    500
    501    public static <K, V> Set<K> keys(Map<K, V> map)
    502    {
    503        if (map == null)
    504            return Collections.emptySet();
    505
    506        return map.keySet();
    507    }
    508
    509    /**
    510     * Gets a value from a map (which may be null).
    511     *
    512     * @param <K>
    513     * @param <V>
    514     * @param map
    515     *         the map to extract from (may be null)
    516     * @param key
    517     * @return the value from the map, or null if the map is null
    518     */
    519
    520    public static <K, V> V get(Map<K, V> map, K key)
    521    {
    522        if (map == null)
    523            return null;
    524
    525        return map.get(key);
    526    }
    527
    528    /**
    529     * Returns true if the method provided is a static method.
    530     */
    531    public static boolean isStatic(Method method)
    532    {
    533        return Modifier.isStatic(method.getModifiers());
    534    }
    535
    536    public static <T> Iterator<T> reverseIterator(final List<T> list)
    537    {
    538        final ListIterator<T> normal = list.listIterator(list.size());
    539
    540        return new Iterator<T>()
    541        {
    542            @Override
    543            public boolean hasNext()
    544            {
    545                return normal.hasPrevious();
    546            }
    547
    548            @Override
    549            public T next()
    550            {
    551                return normal.previous();
    552            }
    553
    554            @Override
    555            public void remove()
    556            {
    557                throw new UnsupportedOperationException();
    558            }
    559        };
    560    }
    561
    562    /**
    563     * Return true if the input string contains the marker for symbols that must be expanded.
    564     */
    565    public static boolean containsSymbols(String input)
    566    {
    567        return InternalCommonsUtils.containsSymbols(input);
    568    }
    569
    570    /**
    571     * Searches the string for the final period ('.') character and returns everything after that. The input string is
    572     * generally a fully qualified class name, though tapestry-core also uses this method for the occasional property
    573     * expression (which is also dot separated). Returns the input string unchanged if it does not contain a period
    574     * character.
    575     */
    576    public static String lastTerm(String input)
    577    {
    578        return InternalCommonsUtils.lastTerm(input);
    579    }
    580
    581    /**
    582     * Searches a class for the "best" constructor, the public constructor with the most parameters. Returns null if
    583     * there are no public constructors. If there is more than one constructor with the maximum number of parameters, it
    584     * is not determined which will be returned (don't build a class like that!). In addition, if a constructor is
    585     * annotated with {@link org.apache.tapestry5.ioc.annotations.Inject}, it will be used (no check for multiple such
    586     * constructors is made, only at most a single constructor should have the annotation).
    587     *
    588     * @param clazz
    589     *         to search for a constructor for
    590     * @return the constructor to be used to instantiate the class, or null if no appropriate constructor was found
    591     */
    592    public static Constructor findAutobuildConstructor(Class clazz)
    593    {
    594        Constructor[] constructors = clazz.getConstructors();
    595
    596        switch (constructors.length)
    597        {
    598            case 1:
    599
    600                return constructors[0];
    601
    602            case 0:
    603
    604                return null;
    605
    606            default:
    607                break;
    608        }
    609
    610        Constructor standardConstructor = findConstructorByAnnotation(constructors, Inject.class);
    611        Constructor javaxConstructor = findConstructorByAnnotation(constructors, javax.inject.Inject.class);
    612
    613        if (standardConstructor != null && javaxConstructor != null)
    614            throw new IllegalArgumentException(
    615                    String.format(
    616                            "Too many autobuild constructors found: use either @%s or @%s annotation to mark a single constructor for autobuilding.",
    617                            Inject.class.getName(), javax.inject.Inject.class.getName()));
    618
    619        if (standardConstructor != null)
    620        {
    621            return standardConstructor;
    622        }
    623
    624        if (javaxConstructor != null)
    625        {
    626            return javaxConstructor;
    627        }
    628
    629        // Choose a constructor with the most parameters.
    630
    631        Comparator<Constructor> comparator = new Comparator<Constructor>()
    632        {
    633            @Override
    634            public int compare(Constructor o1, Constructor o2)
    635            {
    636                return o2.getParameterTypes().length - o1.getParameterTypes().length;
    637            }
    638        };
    639
    640        Arrays.sort(constructors, comparator);
    641
    642        return constructors[0];
    643    }
    644
    645    private static <T extends Annotation> Constructor findConstructorByAnnotation(Constructor[] constructors,
    646                                                                                  Class<T> annotationClass)
    647    {
    648        for (Constructor c : constructors)
    649        {
    650            if (c.getAnnotation(annotationClass) != null)
    651                return c;
    652        }
    653
    654        return null;
    655    }
    656
    657    /**
    658     * Adds a value to a specially organized map where the values are lists of objects. This somewhat simulates a map
    659     * that allows multiple values for the same key.
    660     *
    661     * @param map
    662     *         to store value into
    663     * @param key
    664     *         for which a value is added
    665     * @param value
    666     *         to add
    667     * @param <K>
    668     *         the type of key
    669     * @param <V>
    670     *         the type of the list
    671     */
    672    public static <K, V> void addToMapList(Map<K, List<V>> map, K key, V value)
    673    {
    674        InternalCommonsUtils.addToMapList(map, key, value);
    675    }
    676
    677    /**
    678     * Validates that the marker annotation class had a retention policy of runtime.
    679     *
    680     * @param markerClass
    681     *         the marker annotation class
    682     */
    683    public static void validateMarkerAnnotation(Class markerClass)
    684    {
    685        Retention policy = (Retention) markerClass.getAnnotation(Retention.class);
    686
    687        if (policy != null && policy.value() == RetentionPolicy.RUNTIME)
    688            return;
    689
    690        throw new IllegalArgumentException(UtilMessages.badMarkerAnnotation(markerClass));
    691    }
    692
    693    public static void validateMarkerAnnotations(Class[] markerClasses)
    694    {
    695        for (Class markerClass : markerClasses)
    696            validateMarkerAnnotation(markerClass);
    697    }
    698
    699    public static void close(Closeable stream)
    700    {
    701        if (stream != null)
    702            try
    703            {
    704                stream.close();
    705            } catch (IOException ex)
    706            {
    707                // Ignore.
    708            }
    709    }
    710
    711    /**
    712     * Extracts the message from an exception. If the exception's message is null, returns the exceptions class name.
    713     *
    714     * @param exception
    715     *         to extract message from
    716     * @return message or class name
    717     * @deprecated Deprecated in 5.4; use {@link ExceptionUtils#toMessage(Throwable)} instead.
    718     */
    719    // Cause it gets used a lot outside of Tapestry proper even though it is internal.
    720    public static String toMessage(Throwable exception)
    721    {
    722        return ExceptionUtils.toMessage(exception);
    723    }
    724
    725    public static void validateConstructorForAutobuild(Constructor constructor)
    726    {
    727        Class clazz = constructor.getDeclaringClass();
    728
    729        if (!Modifier.isPublic(clazz.getModifiers()))
    730            throw new IllegalArgumentException(String.format(
    731                    "Class %s is not a public class and may not be autobuilt.", clazz.getName()));
    732
    733        if (!Modifier.isPublic(constructor.getModifiers()))
    734            throw new IllegalArgumentException(
    735                    String.format(
    736                            "Constructor %s is not public and may not be used for autobuilding an instance of the class. "
    737                                    + "You should make the constructor public, or mark an alternate public constructor with the @Inject annotation.",
    738                            constructor));
    739    }
    740
    741    /**
    742     * @since 5.3
    743     */
    744    public static final Mapper<Class, AnnotationProvider> CLASS_TO_AP_MAPPER = new Mapper<Class, AnnotationProvider>()
    745    {
    746        @Override
    747        public AnnotationProvider map(final Class element)
    748        {
    749            return toAnnotationProvider(element);
    750        }
    751
    752    };
    753
    754    /**
    755     * @since 5.3
    756     */
    757    public static AnnotationProvider toAnnotationProvider(final Class element)
    758    {
    759        return InternalCommonsUtils.toAnnotationProvider(element);
    760    }
    761
    762    /**
    763     * @since 5.3
    764     */
    765    public static final Mapper<Method, AnnotationProvider> METHOD_TO_AP_MAPPER = new Mapper<Method, AnnotationProvider>()
    766    {
    767        @Override
    768        public AnnotationProvider map(final Method element)
    769        {
    770            return toAnnotationProvider(element);
    771        }
    772    };
    773
    774    public static final Method findMethod(Class containingClass, String methodName, Class... parameterTypes)
    775    {
    776        if (containingClass == null)
    777            return null;
    778
    779        try
    780        {
    781            return containingClass.getMethod(methodName, parameterTypes);
    782        } catch (SecurityException ex)
    783        {
    784            throw new RuntimeException(ex);
    785        } catch (NoSuchMethodException ex)
    786        {
    787            return null;
    788        }
    789    }
    790
    791    /**
    792     * @since 5.3
    793     */
    794    public static ServiceDef3 toServiceDef3(ServiceDef sd)
    795    {
    796        if (sd instanceof ServiceDef3)
    797            return (ServiceDef3) sd;
    798
    799        final ServiceDef2 sd2 = toServiceDef2(sd);
    800
    801        return new ServiceDef3()
    802        {
    803            // ServiceDef3 methods:
    804
    805            @Override
    806            public AnnotationProvider getClassAnnotationProvider()
    807            {
    808                return toAnnotationProvider(getServiceInterface());
    809            }
    810
    811            @Override
    812            public AnnotationProvider getMethodAnnotationProvider(final String methodName, final Class... argumentTypes)
    813            {
    814                return toAnnotationProvider(findMethod(getServiceInterface(), methodName, argumentTypes));
    815            }
    816
    817            @Override
    818            public Class getServiceImplementation()
    819            {
    820                return null;
    821            }
    822
    823            // ServiceDef2 methods:
    824
    825            @Override
    826            public boolean isPreventDecoration()
    827            {
    828                return sd2.isPreventDecoration();
    829            }
    830
    831            @Override
    832            public ObjectCreator createServiceCreator(ServiceBuilderResources resources)
    833            {
    834                return sd2.createServiceCreator(resources);
    835            }
    836
    837            @Override
    838            public String getServiceId()
    839            {
    840                return sd2.getServiceId();
    841            }
    842
    843            @Override
    844            public Set<Class> getMarkers()
    845            {
    846                return sd2.getMarkers();
    847            }
    848
    849            @Override
    850            public Class getServiceInterface()
    851            {
    852                return sd2.getServiceInterface();
    853            }
    854
    855            @Override
    856            public String getServiceScope()
    857            {
    858                return sd2.getServiceScope();
    859            }
    860
    861            @Override
    862            public boolean isEagerLoad()
    863            {
    864                return sd2.isEagerLoad();
    865            }
    866
    867        };
    868    }
    869
    870    public static ServiceDef2 toServiceDef2(final ServiceDef sd)
    871    {
    872        if (sd instanceof ServiceDef2)
    873            return (ServiceDef2) sd;
    874
    875        return new ServiceDef2()
    876        {
    877            // ServiceDef2 methods:
    878
    879            @Override
    880            public boolean isPreventDecoration()
    881            {
    882                return false;
    883            }
    884
    885            // ServiceDef methods:
    886
    887            @Override
    888            public ObjectCreator createServiceCreator(ServiceBuilderResources resources)
    889            {
    890                return sd.createServiceCreator(resources);
    891            }
    892
    893            @Override
    894            public String getServiceId()
    895            {
    896                return sd.getServiceId();
    897            }
    898
    899            @Override
    900            public Set<Class> getMarkers()
    901            {
    902                return sd.getMarkers();
    903            }
    904
    905            @Override
    906            public Class getServiceInterface()
    907            {
    908                return sd.getServiceInterface();
    909            }
    910
    911            @Override
    912            public String getServiceScope()
    913            {
    914                return sd.getServiceScope();
    915            }
    916
    917            @Override
    918            public boolean isEagerLoad()
    919            {
    920                return sd.isEagerLoad();
    921            }
    922
    923            @Override
    924            public String toString()
    925            {
    926                return sd.toString();
    927            }
    928
    929            @Override
    930            public int hashCode()
    931            {
    932                final int prime = 31;
    933                int result = 1;
    934                result = prime * result + ((getServiceId() == null) ? 0 : getServiceId().hashCode());
    935                return result;
    936            }
    937
    938            @Override
    939            public boolean equals(Object obj)
    940            {
    941                if (this == obj) { return true; }
    942                if (obj == null) { return false; }
    943                if (!(obj instanceof ServiceDefImpl)) { return false; }
    944                ServiceDef other = (ServiceDef) obj;
    945                if (getServiceId() == null)
    946                {
    947                    if (other.getServiceId() != null) { return false; }
    948                }
    949                else if (!getServiceId().equals(other.getServiceId())) { return false; }
    950                return true;
    951            }
    952
    953        };
    954    }
    955
    956    public static ModuleDef2 toModuleDef2(final ModuleDef md)
    957    {
    958        if (md instanceof ModuleDef2)
    959            return (ModuleDef2) md;
    960
    961        return new ModuleDef2()
    962        {
    963            @Override
    964            public Set<AdvisorDef> getAdvisorDefs()
    965            {
    966                return Collections.emptySet();
    967            }
    968
    969            @Override
    970            public Class getBuilderClass()
    971            {
    972                return md.getBuilderClass();
    973            }
    974
    975            @Override
    976            public Set<ContributionDef> getContributionDefs()
    977            {
    978                return md.getContributionDefs();
    979            }
    980
    981            @Override
    982            public Set<DecoratorDef> getDecoratorDefs()
    983            {
    984                return md.getDecoratorDefs();
    985            }
    986
    987            @Override
    988            public String getLoggerName()
    989            {
    990                return md.getLoggerName();
    991            }
    992
    993            @Override
    994            public ServiceDef getServiceDef(String serviceId)
    995            {
    996                return md.getServiceDef(serviceId);
    997            }
    998
    999            @Override
    1000            public Set<String> getServiceIds()
    1001            {
    1002                return md.getServiceIds();
    1003            }
    1004
    1005            @Override
    1006            public Set<StartupDef> getStartups()
    1007            {
    1008                return Collections.emptySet();
    1009            }
    1010        };
    1011    }
    1012
    1013    /**
    1014     * @since 5.1.0.2
    1015     */
    1016    public static ServiceLifecycle2 toServiceLifecycle2(final ServiceLifecycle lifecycle)
    1017    {
    1018        if (lifecycle instanceof ServiceLifecycle2)
    1019            return (ServiceLifecycle2) lifecycle;
    1020
    1021        return new ServiceLifecycle2()
    1022        {
    1023            @Override
    1024            public boolean requiresProxy()
    1025            {
    1026                return true;
    1027            }
    1028
    1029            @Override
    1030            public Object createService(ServiceResources resources, ObjectCreator creator)
    1031            {
    1032                return lifecycle.createService(resources, creator);
    1033            }
    1034
    1035            @Override
    1036            public boolean isSingleton()
    1037            {
    1038                return lifecycle.isSingleton();
    1039            }
    1040        };
    1041    }
    1042
    1043    /**
    1044     * @since 5.2.0
    1045     */
    1046    public static <T extends Comparable<T>> List<T> matchAndSort(Collection<? extends T> collection,
    1047                                                                 Predicate<T> predicate)
    1048    {
    1049        assert predicate != null;
    1050
    1051        List<T> result = CollectionFactory.newList();
    1052
    1053        for (T object : collection)
    1054        {
    1055            if (predicate.accept(object))
    1056                result.add(object);
    1057        }
    1058
    1059        Collections.sort(result);
    1060
    1061        return result;
    1062    }
    1063
    1064    /**
    1065     * @since 5.2.0
    1066     */
    1067    public static ContributionDef2 toContributionDef2(final ContributionDef contribution)
    1068    {
    1069        if (contribution instanceof ContributionDef2)
    1070            return (ContributionDef2) contribution;
    1071
    1072        return new ContributionDef2()
    1073        {
    1074
    1075            @Override
    1076            public Set<Class> getMarkers()
    1077            {
    1078                return Collections.emptySet();
    1079            }
    1080
    1081            @Override
    1082            public Class getServiceInterface()
    1083            {
    1084                return null;
    1085            }
    1086
    1087            @Override
    1088            public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
    1089                                   Configuration configuration)
    1090            {
    1091                contribution.contribute(moduleSource, resources, configuration);
    1092            }
    1093
    1094            @Override
    1095            public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
    1096                                   OrderedConfiguration configuration)
    1097            {
    1098                contribution.contribute(moduleSource, resources, configuration);
    1099            }
    1100
    1101            @Override
    1102            public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
    1103                                   MappedConfiguration configuration)
    1104            {
    1105                contribution.contribute(moduleSource, resources, configuration);
    1106            }
    1107
    1108            @Override
    1109            public String getServiceId()
    1110            {
    1111                return contribution.getServiceId();
    1112            }
    1113
    1114            @Override
    1115            public String toString()
    1116            {
    1117                return contribution.toString();
    1118            }
    1119        };
    1120    }
    1121
    1122    public static ContributionDef3 toContributionDef3(ContributionDef contribution)
    1123    {
    1124
    1125        if (contribution instanceof ContributionDef2)
    1126        {
    1127            return (ContributionDef3) contribution;
    1128        }
    1129
    1130        final ContributionDef2 cd2 = toContributionDef2(contribution);
    1131
    1132        return new ContributionDef3()
    1133        {
    1134            @Override
    1135            public boolean isOptional()
    1136            {
    1137                return false;
    1138            }
    1139
    1140            @Override
    1141            public String getServiceId()
    1142            {
    1143                return cd2.getServiceId();
    1144            }
    1145
    1146            @Override
    1147            public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources, Configuration configuration)
    1148            {
    1149                cd2.contribute(moduleSource, resources, configuration);
    1150            }
    1151
    1152            @Override
    1153            public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources, OrderedConfiguration configuration)
    1154            {
    1155                cd2.contribute(moduleSource, resources, configuration);
    1156            }
    1157
    1158            @Override
    1159            public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources, MappedConfiguration configuration)
    1160            {
    1161                cd2.contribute(moduleSource, resources, configuration);
    1162            }
    1163
    1164            @Override
    1165            public Set<Class> getMarkers()
    1166            {
    1167                return cd2.getMarkers();
    1168            }
    1169
    1170            @Override
    1171            public Class getServiceInterface()
    1172            {
    1173                return cd2.getServiceInterface();
    1174            }
    1175
    1176            @Override
    1177            public String toString()
    1178            {
    1179                return cd2.toString();
    1180            }
    1181        };
    1182    }
    1183
    1184    /**
    1185     * @since 5.2.2
    1186     */
    1187    public static AdvisorDef2 toAdvisorDef2(final AdvisorDef advisor)
    1188    {
    1189        if (advisor instanceof AdvisorDef2)
    1190            return (AdvisorDef2) advisor;
    1191
    1192        return new AdvisorDef2()
    1193        {
    1194
    1195            @Override
    1196            public ServiceAdvisor createAdvisor(ModuleBuilderSource moduleSource, ServiceResources resources)
    1197            {
    1198                return advisor.createAdvisor(moduleSource, resources);
    1199            }
    1200
    1201            @Override
    1202            public String getAdvisorId()
    1203            {
    1204                return advisor.getAdvisorId();
    1205            }
    1206
    1207            @Override
    1208            public String[] getConstraints()
    1209            {
    1210                return advisor.getConstraints();
    1211            }
    1212
    1213            @Override
    1214            public boolean matches(ServiceDef serviceDef)
    1215            {
    1216                return advisor.matches(serviceDef);
    1217            }
    1218
    1219            @Override
    1220            public Set<Class> getMarkers()
    1221            {
    1222                return Collections.emptySet();
    1223            }
    1224
    1225            @Override
    1226            public Class getServiceInterface()
    1227            {
    1228                return null;
    1229            }
    1230
    1231            @Override
    1232            public String toString()
    1233            {
    1234                return advisor.toString();
    1235            }
    1236        };
    1237    }
    1238
    1239    /**
    1240     * @since 5.2.2
    1241     */
    1242    public static DecoratorDef2 toDecoratorDef2(final DecoratorDef decorator)
    1243    {
    1244        if (decorator instanceof DecoratorDef2)
    1245            return (DecoratorDef2) decorator;
    1246
    1247        return new DecoratorDef2()
    1248        {
    1249
    1250            @Override
    1251            public ServiceDecorator createDecorator(ModuleBuilderSource moduleSource, ServiceResources resources)
    1252            {
    1253                return decorator.createDecorator(moduleSource, resources);
    1254            }
    1255
    1256            @Override
    1257            public String[] getConstraints()
    1258            {
    1259                return decorator.getConstraints();
    1260            }
    1261
    1262            @Override
    1263            public String getDecoratorId()
    1264            {
    1265                return decorator.getDecoratorId();
    1266            }
    1267
    1268            @Override
    1269            public boolean matches(ServiceDef serviceDef)
    1270            {
    1271                return decorator.matches(serviceDef);
    1272            }
    1273
    1274            @Override
    1275            public Set<Class> getMarkers()
    1276            {
    1277                return Collections.emptySet();
    1278            }
    1279
    1280            @Override
    1281            public Class getServiceInterface()
    1282            {
    1283                return null;
    1284            }
    1285
    1286            @Override
    1287            public String toString()
    1288            {
    1289                return decorator.toString();
    1290            }
    1291        };
    1292    }
    1293
    1294    /**
    1295     * Determines if the indicated class is stored as a locally accessible file
    1296     * (and not, typically, as a file inside a JAR). This is related to automatic
    1297     * reloading of services.
    1298     *
    1299     * @since 5.2.0
    1300     */
    1301    public static boolean isLocalFile(Class clazz)
    1302    {
    1303        String path = PlasticInternalUtils.toClassPath(clazz.getName());
    1304
    1305        ClassLoader loader = clazz.getClassLoader();
    1306
    1307        // System classes have no visible class loader, and are not local files.
    1308
    1309        if (loader == null)
    1310            return false;
    1311
    1312        URL classFileURL = loader.getResource(path);
    1313
    1314        return classFileURL != null && classFileURL.getProtocol().equals("file");
    1315    }
    1316
    1317    /**
    1318     * Wraps a {@link Coercion} as a {@link Mapper}.
    1319     *
    1320     * @since 5.2.0
    1321     */
    1322    public static <S, T> Mapper<S, T> toMapper(final Coercion<S, T> coercion)
    1323    {
    1324        assert coercion != null;
    1325
    1326        return new Mapper<S, T>()
    1327        {
    1328            @Override
    1329            public T map(S value)
    1330            {
    1331                return coercion.coerce(value);
    1332            }
    1333        };
    1334    }
    1335
    1336    private static final AtomicLong uuidGenerator = new AtomicLong(System.nanoTime());
    1337
    1338    /**
    1339     * Generates a unique value for the current execution of the application. This initial UUID value
    1340     * is not easily predictable; subsequent UUIDs are allocated in ascending series.
    1341     *
    1342     * @since 5.2.0
    1343     */
    1344    public static long nextUUID()
    1345    {
    1346        return uuidGenerator.incrementAndGet();
    1347    }
    1348
    1349    /**
    1350     * Extracts the service id from the passed annotated element. First the {@link ServiceId} annotation is checked.
    1351     * If present, its value is returned. Otherwise {@link Named} annotation is checked. If present, its value is
    1352     * returned.
    1353     * If neither of the annotations is present, <code>null</code> value is returned
    1354     *
    1355     * @param annotated
    1356     *         annotated element to get annotations from
    1357     * @since 5.3
    1358     */
    1359    public static String getServiceId(AnnotatedElement annotated)
    1360    {
    1361        ServiceId serviceIdAnnotation = annotated.getAnnotation(ServiceId.class);
    1362
    1363        if (serviceIdAnnotation != null)
    1364        {
    1365            return serviceIdAnnotation.value();
    1366        }
    1367
    1368        Named namedAnnotation = annotated.getAnnotation(Named.class);
    1369
    1370        if (namedAnnotation != null)
    1371        {
    1372            String value = namedAnnotation.value();
    1373
    1374            if (InternalCommonsUtils.isNonBlank(value))
    1375            {
    1376                return value;
    1377            }
    1378        }
    1379
    1380        return null;
    1381    }
    1382
    1383
    1384    public static AnnotationProvider toAnnotationProvider(final Method element)
    1385    {
    1386        return InternalCommonsUtils.toAnnotationProvider(element);
    1387    }
    1388
    1389    public static <T> ObjectCreator<T> createConstructorConstructionPlan(final OperationTracker tracker, final ObjectLocator locator,
    1390                                                                         final InjectionResources resources,
    1391                                                                         final Logger logger,
    1392                                                                         final String description,
    1393                                                                         final Constructor<T> constructor)
    1394    {
    1395        return tracker.invoke(String.format("Creating plan to instantiate %s via %s",
    1396                constructor.getDeclaringClass().getName(),
    1397                constructor), new Invokable<ObjectCreator<T>>()
    1398        {
    1399            @Override
    1400            public ObjectCreator<T> invoke()
    1401            {
    1402                validateConstructorForAutobuild(constructor);
    1403
    1404                ObjectCreator[] constructorParameters = calculateParameters(locator, resources, constructor.getParameterTypes(), constructor.getGenericParameterTypes(), constructor.getParameterAnnotations(), tracker);
    1405
    1406                Invokable<T> core = new ConstructorInvoker<T>(constructor, constructorParameters);
    1407
    1408                Invokable<T> wrapped = logger == null ? core : new LoggingInvokableWrapper<T>(logger, description, core);
    1409
    1410                ConstructionPlan<T> plan = new ConstructionPlan(tracker, description, wrapped);
    1411
    1412                extendPlanForInjectedFields(plan, tracker, locator, resources, constructor.getDeclaringClass());
    1413
    1414                extendPlanForPostInjectionMethods(plan, tracker, locator, resources, constructor.getDeclaringClass());
    1415
    1416                return plan;
    1417            }
    1418        });
    1419    }
    1420
    1421    private static <T> void extendPlanForInjectedFields(final ConstructionPlan<T> plan, OperationTracker tracker, final ObjectLocator locator, final InjectionResources resources, Class<T> instantiatedClass)
    1422    {
    1423        Class clazz = instantiatedClass;
    1424
    1425        while (clazz != Object.class)
    1426        {
    1427            Field[] fields = clazz.getDeclaredFields();
    1428
    1429            for (final Field f : fields)
    1430            {
    1431                // Ignore all static and final fields.
    1432
    1433                int fieldModifiers = f.getModifiers();
    1434
    1435                if (Modifier.isStatic(fieldModifiers) || Modifier.isFinal(fieldModifiers))
    1436                    continue;
    1437
    1438                final AnnotationProvider ap = new AnnotationProvider()
    1439                {
    1440                    @Override
    1441                    public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
    1442                    {
    1443                        return f.getAnnotation(annotationClass);
    1444                    }
    1445                };
    1446
    1447                String description = String.format("Calculating possible injection value for field %s.%s (%s)",
    1448                        clazz.getName(), f.getName(),
    1449                        PlasticUtils.toTypeName(f.getType()));
    1450
    1451                tracker.run(description, new Runnable()
    1452                {
    1453                    @Override
    1454                    public void run()
    1455                    {
    1456                        final Class<?> fieldType = f.getType();
    1457
    1458                        InjectService is = ap.getAnnotation(InjectService.class);
    1459                        if (is != null)
    1460                        {
    1461                            addInjectPlan(plan, f, locator.getService(is.value(), fieldType));
    1462                            return;
    1463                        }
    1464
    1465                        if (ap.getAnnotation(Inject.class) != null || ap.getAnnotation(InjectResource.class) != null)
    1466                        {
    1467                            Object value = resources.findResource(fieldType, f.getGenericType());
    1468
    1469                            if (value != null)
    1470                            {
    1471                                addInjectPlan(plan, f, value);
    1472                                return;
    1473                            }
    1474
    1475                            addInjectPlan(plan, f, locator.getObject(fieldType, ap));
    1476                            return;
    1477                        }
    1478
    1479                        if (ap.getAnnotation(javax.inject.Inject.class) != null)
    1480                        {
    1481                            Named named = ap.getAnnotation(Named.class);
    1482
    1483                            if (named == null)
    1484                            {
    1485                                addInjectPlan(plan, f, locator.getObject(fieldType, ap));
    1486                            } else
    1487                            {
    1488                                addInjectPlan(plan, f, locator.getService(named.value(), fieldType));
    1489                            }
    1490
    1491                            return;
    1492                        }
    1493
    1494                        // Ignore fields that do not have the necessary annotation.
    1495
    1496                    }
    1497                });
    1498            }
    1499
    1500            clazz = clazz.getSuperclass();
    1501        }
    1502    }
    1503
    1504    private static <T> void addInjectPlan(ConstructionPlan<T> plan, final Field field, final Object injectedValue)
    1505    {
    1506        plan.add(new InitializationPlan<T>()
    1507        {
    1508            @Override
    1509            public String getDescription()
    1510            {
    1511                return String.format("Injecting %s into field %s of class %s.",
    1512                        injectedValue,
    1513                        field.getName(),
    1514                        field.getDeclaringClass().getName());
    1515            }
    1516
    1517            @Override
    1518            public void initialize(T instance)
    1519            {
    1520                inject(instance, field, injectedValue);
    1521            }
    1522        });
    1523    }
    1524
    1525    private static boolean hasAnnotation(AccessibleObject member, Class<? extends Annotation> annotationType)
    1526    {
    1527        return member.getAnnotation(annotationType) != null;
    1528    }
    1529
    1530    private static <T> void extendPlanForPostInjectionMethods(ConstructionPlan<T> plan, OperationTracker tracker, ObjectLocator locator, InjectionResources resources, Class<T> instantiatedClass)
    1531    {
    1532        for (Method m : instantiatedClass.getMethods())
    1533        {
    1534            if (hasAnnotation(m, PostInjection.class) || hasAnnotation(m, PostConstruct.class))
    1535            {
    1536                extendPlanForPostInjectionMethod(plan, tracker, locator, resources, m);
    1537            }
    1538        }
    1539    }
    1540
    1541    private static void extendPlanForPostInjectionMethod(final ConstructionPlan<?> plan, final OperationTracker tracker, final ObjectLocator locator, final InjectionResources resources, final Method method)
    1542    {
    1543        tracker.run("Computing parameters for post-injection method " + method,
    1544                new Runnable()
    1545                {
    1546                    @Override
    1547                    public void run()
    1548                    {
    1549                        final ObjectCreator[] parameters = calculateParametersForMethod(method, locator,
    1550                                resources, tracker);
    1551
    1552                        plan.add(new InitializationPlan<Object>()
    1553                        {
    1554                            @Override
    1555                            public String getDescription()
    1556                            {
    1557                                return "Invoking " + method;
    1558                            }
    1559
    1560                            @Override
    1561                            public void initialize(Object instance)
    1562                            {
    1563                                Throwable fail = null;
    1564
    1565                                Object[] realized = realizeObjects(parameters);
    1566
    1567                                try
    1568                                {
    1569                                    method.invoke(instance, realized);
    1570                                } catch (InvocationTargetException ex)
    1571                                {
    1572                                    fail = ex.getTargetException();
    1573                                } catch (Exception ex)
    1574                                {
    1575                                    fail = ex;
    1576                                }
    1577
    1578                                if (fail != null)
    1579                                {
    1580                                    throw new RuntimeException(String
    1581                                            .format("Exception invoking method %s: %s", method, ExceptionUtils.toMessage(fail)), fail);
    1582                                }
    1583                            }
    1584                        });
    1585                    }
    1586                });
    1587    }
    1588
    1589
    1590    public static <T> ObjectCreator<T> createMethodInvocationPlan(final OperationTracker tracker, final ObjectLocator locator,
    1591                                                                  final InjectionResources resources,
    1592                                                                  final Logger logger,
    1593                                                                  final String description,
    1594                                                                  final Object instance,
    1595                                                                  final Method method)
    1596    {
    1597
    1598        return tracker.invoke("Creating plan to invoke " + method, new Invokable<ObjectCreator<T>>()
    1599        {
    1600            @Override
    1601            public ObjectCreator<T> invoke()
    1602            {
    1603                ObjectCreator[] methodParameters = calculateParametersForMethod(method, locator, resources, tracker);
    1604
    1605                Invokable<T> core = new MethodInvoker<T>(instance, method, methodParameters);
    1606
    1607                Invokable<T> wrapped = logger == null ? core : new LoggingInvokableWrapper<T>(logger, description, core);
    1608
    1609                return new ConstructionPlan(tracker, description, wrapped);
    1610            }
    1611        });
    1612    }
    1613
    1614    /**
    1615     * @since 5.3.1, 5.4
    1616     */
    1617    public final static Mapper<ObjectCreator, Object> CREATE_OBJECT = new Mapper<ObjectCreator, Object>()
    1618    {
    1619        @Override
    1620        public Object map(ObjectCreator element)
    1621        {
    1622            return element.createObject();
    1623        }
    1624    };
    1625
    1626    /**
    1627     * @since 5.3.1, 5.4
    1628     */
    1629    public static Object[] realizeObjects(ObjectCreator[] creators)
    1630    {
    1631        return F.flow(creators).map(CREATE_OBJECT).toArray(Object.class);
    1632    }
    1633
    1634    /**
    1635     * Extracts the string keys from a map and returns them in sorted order. The keys are converted to strings.
    1636     *
    1637     * @param map
    1638     *         the map to extract keys from (may be null)
    1639     * @return the sorted keys, or the empty set if map is null
    1640     */
    1641
    1642    public static List<String> sortedKeys(Map map)
    1643    {
    1644        return InternalCommonsUtils.sortedKeys(map);
    1645    }
    1646
    1647    /**
    1648     * Capitalizes the string, and inserts a space before each upper case character (or sequence of upper case
    1649     * characters). Thus "userId" becomes "User Id", etc. Also, converts underscore into space (and capitalizes the
    1650     * following word), thus "user_id" also becomes "User Id".
    1651     */
    1652    public static String toUserPresentable(String id)
    1653    {
    1654        return InternalCommonsUtils.toUserPresentable(id);
    1655    }
    1656
    1657    /**
    1658     * Used to convert a property expression into a key that can be used to locate various resources (Blocks, messages,
    1659     * etc.). Strips out any punctuation characters, leaving just words characters (letters, number and the
    1660     * underscore).
    1661     *
    1662     * @param expression a property expression
    1663     * @return the expression with punctuation removed
    1664     */
    1665    public static String extractIdFromPropertyExpression(String expression)
    1666    {
    1667        return InternalCommonsUtils.extractIdFromPropertyExpression(expression);
    1668    }
    1669
    1670    /**
    1671     * Looks for a label within the messages based on the id. If found, it is used, otherwise the name is converted to a
    1672     * user presentable form.
    1673     */
    1674    public static String defaultLabel(String id, Messages messages, String propertyExpression)
    1675    {
    1676        return InternalCommonsUtils.defaultLabel(id, messages, propertyExpression);
    1677    }
    1678
    1679    public static String replace(String input, Pattern pattern, String replacement)
    1680    {
    1681        return InternalCommonsUtils.replace(input, pattern, replacement);
    1682    }
    1683
    1684}