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