org.agiso.core.i18n.util.I18nUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.agiso.core.i18n.util.I18nUtils.java

Source

/* org.agiso.core.i18n.util.I18nUtils (20-08-2012)
 * 
 * I18nUtils.java
 * 
 * Copyright 2012 agiso.org.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.agiso.core.i18n.util;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;

import org.agiso.core.i18n.annotation.I18n;
import org.agiso.core.i18n.provider.ILocaleProvider;
import org.agiso.core.i18n.provider.IMessageProvider;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.google.common.collect.ImmutableMap;

import net.jodah.typetools.TypeResolver;

/**
 * Klasa narzdziowa dostarczajca funkcjonalnoci zwizanych z wielojzykowoci
 * acuchw znakw (komunikaty, wiadomoci, etc...)
 * 
 * @author Karol Kopacz
 * @since 1.0
 */
public abstract class I18nUtils {
    /**
     * Bazowy interfejs wyliczenia zestawu komunikatw wykorzystywany do
     * znacznikowania tych wylicze.
     */
    public interface I18nId {
    }

    //   --------------------------------------------------------------------------
    //   Obsuga LocaleProvider'a i MessageProvider'a
    //   --------------------------------------------------------------------------
    private static final Method METHOD_TO_LANGUAGE_TAG;

    private static ILocaleProvider localeProvider;
    private static final ILocaleProvider internalLocaleProivder = new ILocaleProvider() {
        @Override
        public Locale getLocale() {
            return Locale.getDefault();
        }
    };

    private static IMessageProvider[] messageProviders;
    private static final IMessageProvider[] internalMessageProviders = new IMessageProvider[] {
            new SimpleMessageProvider() };

    static {
        Method method_toLanguageTag = null;
        try {
            method_toLanguageTag = Locale.class.getMethod("toLanguageTag");
        } catch (Exception e) {
        }
        METHOD_TO_LANGUAGE_TAG = method_toLanguageTag;

        localeProvider = internalLocaleProivder;
        messageProviders = internalMessageProviders;
    }

    //   --------------------------------------------------------------------------
    /**
     * Zwraca dostarczyciela lokalizacji wykorzystywanego przez klas
     * narzdziow, lub <code>null</code> jeli dostarczyciel nie by ustawiony
     * i jest wykorzystywany mechanizm wbudowany.
     */
    public static ILocaleProvider getLocaleProvider() {
        if (localeProvider == internalLocaleProivder) {
            return null;
        } else {
            return localeProvider;
        }
    }

    /**
     * Ustawia dostarczyciela lokalizacji wykorzystywanego przez klas narzdziow.
     * Jeli metoda nie zostanie wywoana, lokalizacja zwracana bdzie zgodnie
     * z mechanizmem wbudowanym.<br/>
     * Wywoanie metody z wartoci <code>null</code> przywraca mechanizm wbudowany.
     * 
     * @param provider Dostarczyciel lokalizacji.
     */
    public static void setLocaleProvider(ILocaleProvider provider) {
        if (provider == null) {
            localeProvider = internalLocaleProivder;
        } else {
            localeProvider = provider;
        }
    }

    /**
     * Zwraca tablic dostarczycieli wiadomoci wykorzystywanych przez klas
     * narzdziow, lub <code>null</code> jeli dostarczyciele nie byli ustawieni
     * i jest wykorzystywany mechanizm wbudowany.
     */
    public static IMessageProvider[] getMessageProviders() {
        if (messageProviders == internalMessageProviders) {
            return null;
        } else {
            return messageProviders;
        }
    }

    /**
     * Ustawia dostarczycieli wiadomoci wykorzystywanych przez klas narzdziow.
     * Jeli metoda nie zostanie wywoana, zwracane wiadomoci bd generowane
     * zgodnie z mechanizmem wbudowanym (w oparciu o przekazane do wywoa kody i
     * parametry wiadomoci).<br/>
     * Wywoanie metody z wartoci <code>null</code> przywraca mechanizm wbudowany.
     * 
     * @param provider Dostarczyciel wiadomoci.
     */
    public static void setMessageProvider(IMessageProvider provider) {
        setMessageProviders(provider);
    }

    public static void setMessageProviders(IMessageProvider... providers) {
        // Jeli tablica dostarczycieli jest pusta bd zawiera tylko jeden
        // element o wartoci 'null', to przywracamy mechanizm wbudowany:
        if (providers == null || providers.length == 0 || (providers.length == 1 && providers[0] == null)) {
            messageProviders = internalMessageProviders;
        } else {
            messageProviders = providers;
        }
    }

    //   --------------------------------------------------------------------------
    //   Pobieranie lokalizacji i znacznika jzyka
    //   --------------------------------------------------------------------------
    public static Locale getLocale() {
        return localeProvider.getLocale();
    }

    public static String getLocaleLanguageTag() {
        return getLocaleLanguageTag(getLocale());
    }

    public static String getLocaleLanguageTag(Locale locale) {
        if (METHOD_TO_LANGUAGE_TAG != null)
            try {
                return (String) METHOD_TO_LANGUAGE_TAG.invoke(locale);
            } catch (Exception e) {
            }
        return locale.toString();
    }

    //   --------------------------------------------------------------------------
    //   Pobieranie tumacze na podstawie kodw, wylicze, klas, metod i pl
    //   --------------------------------------------------------------------------
    public static String getMessage(String code, Object... args) {
        return getMessage(I18nUtils.getLocale(), code, args);
    }

    public static String getMessage(Locale locale, String code, Object... args) {
        String message = null;
        for (IMessageProvider messageProvider : messageProviders) {
            message = messageProvider.getMessageIfExists(locale, code, args);
            if (message != null) {
                break;
            }
        }
        return message;
    }

    public static String getMessage(Enum<?> e, Object... args) {
        return getMessage(getCode(e), args);
    }

    public static String getMessage(String dscr, Enum<?> e, Object... args) {
        String message = getMessage(getCode(dscr, e), args);
        if (message == null) {
            message = getMessage(getCode(e), args);
        }
        return message;
    }

    public static String getMessage(Locale locale, Enum<?> e, Object... args) {
        return getMessage(locale, getCode(e), args);
    }

    public static String getMessage(Locale locale, String dscr, Enum<?> e, Object... args) {
        String message = getMessage(locale, getCode(dscr, e), args);
        if (message == null) {
            message = getMessage(locale, getCode(dscr, e), args);
        }
        return message;
    }

    public static String getMessage(Class<?> c, Object... args) {
        return getMessage(getCode(c), args);
    }

    public static String getMessage(Locale locale, Class<?> c, Object... args) {
        return getMessage(locale, getCode(c), args);
    }

    public static String getMessage(Method m, Object... args) {
        return getMessage(getCode(m), args);
    }

    public static String getMessage(Locale locale, Method m, Object... args) {
        return getMessage(locale, getCode(m), args);
    }

    public static String getMessage(Field f, Object... args) {
        return getMessage(getCode(f), args);
    }

    public static String getMessage(Locale locale, Field f, Object... args) {
        return getMessage(locale, getCode(f), args);
    }

    public static String getMessage(Class<?> c, String field, Object... args) throws IntrospectionException {
        return getMessage(getCode(c, field), args);
    }

    public static String getMessage(Locale locale, Class<?> c, String field, Object... args)
            throws IntrospectionException {
        return getMessage(locale, getCode(c, field), args);
    }

    //   --------------------------------------------------------------------------
    //   Wyznaczanie kodw I18n wylicze, klas, metod i pl
    //   --------------------------------------------------------------------------
    private static final char CODE_SEPARATOR = '.';
    private static final char DSCR_SEPARATOR = '#';

    public static String getCode(Enum<?> e) {
        try {
            return getCode(e.getClass().getField(e.name()));
        } catch (SecurityException ex) {
            // TODO Auto-generated catch block
            ex.printStackTrace();
        } catch (NoSuchFieldException ex) {
            // TODO Auto-generated catch block
            ex.printStackTrace();
        }
        return e.getDeclaringClass().getName() + CODE_SEPARATOR + e.name();
    }

    public static String getCode(String dscr, Enum<?> e) {
        return getCode(e) + DSCR_SEPARATOR + dscr;
    }

    public static String getCode(Class<?> c) {
        if (c.isAnnotationPresent(I18n.class)) {
            if (c.getAnnotation(I18n.class).value().length() > 0) {
                return c.getAnnotation(I18n.class).value();
            } else {
                return c.getName();
            }
        }
        // else if(c.isAnnotationPresent(I18nRef.class)) {
        //    Object ref = c.getAnnotation(I18nRef.class).value();
        //    if(((Class<?>)ref).isEnum()) {
        //       return getCode((Enum<?>)ref);
        //    } else {
        //       return getCode((Class<?>)ref);
        //    }
        // }
        return c.getName();
    }

    public static String getCode(Method m) {
        if (m.isAnnotationPresent(I18n.class)) {
            if (m.getAnnotation(I18n.class).value().length() > 0) {
                return m.getAnnotation(I18n.class).value();
            } else {
                return m.getDeclaringClass().getCanonicalName() + CODE_SEPARATOR + findGetterFieldName(m);
            }
        }
        // else if(m.isAnnotationPresent(I18nRef.class)) {
        //    Object ref = m.getAnnotation(I18nRef.class).value();
        //    if(((Class<?>)ref).isEnum()) {
        //       return getCode((Enum<?>)ref);
        //    } else {
        //       return getCode((Class<?>)ref);
        //    }
        // }
        return m.getDeclaringClass().getCanonicalName() + CODE_SEPARATOR + findGetterFieldName(m);
    }

    public static String getCode(Field f) {
        if (f.isAnnotationPresent(I18n.class)) {
            if (f.getAnnotation(I18n.class).value().length() > 0) {
                return f.getAnnotation(I18n.class).value();
            } else {
                return f.getDeclaringClass().getName() + CODE_SEPARATOR + f.getName();
            }
        }
        // else if(f.isAnnotationPresent(I18nRef.class)) {
        //    Object ref = f.getAnnotation(I18nRef.class).value();
        //    if(((Class<?>)ref).isEnum()) {
        //       return getCode((Enum<?>)ref);
        //    } else {
        //       return getCode((Class<?>)ref);
        //    }
        // }
        return f.getDeclaringClass().getName() + CODE_SEPARATOR + f.getName();
    }

    public static String getCode(Class<?> c, String field) throws IntrospectionException {
        final String i18nCode = findGetterFieldCode(c, field, false);
        return i18nCode != null ? i18nCode : c.getName() + CODE_SEPARATOR + field;
    }

    public static String findCode(Class<?> c, String field) throws IntrospectionException {
        final String i18nCode = findGetterFieldCode(c, field, true);
        return i18nCode != null ? i18nCode : c.getName() + CODE_SEPARATOR + field;
    }

    @SuppressWarnings("unchecked")
    public static <T, R extends Serializable> String getCode(Function<T, R> f) throws IntrospectionException {
        final Class<?>[] arguments = TypeResolver.resolveRawArguments(Function.class, f.getClass());

        return getCode((Class<T>) arguments[0], f);
    }

    public static <T, R extends Serializable> String getCode(Class<T> c, Function<T, R> f)
            throws IntrospectionException {
        Recorder<T> recorder = RecordingObject.create(c);
        T object = recorder.getObject();

        f.apply(object);

        String name = recorder.getCurrentPropertyName();
        name = uncapitalize(banishGetterSetters(name));

        return getCode(c, name);
    }

    //   --------------------------------------------------------------------------
    private static String banishGetterSetters(String name) {
        return name.replaceAll("^(get|set)", "");
    }

    public static String toSnakeCase(String s) {
        return s.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
    }

    public static String uncapitalize(String s) {
        return Character.toLowerCase(s.charAt(0)) + s.substring(1);
    }

    private static String findGetterFieldName(Method m) {
        String name = m.getName();
        if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3))) {
            if (name.length() == 4) {
                name = String.valueOf(Character.toLowerCase(name.charAt(3)));
            } else {
                name = String.valueOf(Character.toLowerCase(name.charAt(3))) + name.substring(4);
            }
        } else if (name.length() > 2 && name.startsWith("is") && Character.isUpperCase(name.charAt(2))) {
            if (name.length() == 3) {
                name = String.valueOf(Character.toLowerCase(name.charAt(2)));
            } else {
                name = String.valueOf(Character.toLowerCase(name.charAt(2))) + name.substring(3);
            }
        }
        return name;
    }

    private static String findGetterFieldCode(Class<?> c, String field, boolean reflectionCheck)
            throws IntrospectionException {
        for (PropertyDescriptor pd : Introspector.getBeanInfo(c).getPropertyDescriptors()) {
            if (pd.getName().equals(field)) {
                final Method g = pd.getReadMethod();
                if (g != null) {
                    // Jeli jest adnotacja I18n na metodzie odczytujcej pole, to pobieranie
                    // pobieranie jej klucza (okrelonego przez 'value') lub klucza domylnego:
                    if (g.isAnnotationPresent(I18n.class)) {
                        if (g.getAnnotation(I18n.class).value().length() > 0) {
                            return g.getAnnotation(I18n.class).value();
                        } else {
                            return g.getDeclaringClass().getName() + CODE_SEPARATOR + field;
                        }
                    } else if (reflectionCheck) {
                        // Pole nie jest opisane adnotacj I18n. Jeli do wyszukania maj by
                        // wykorzystane mechanizmy refleksji, to sprawdzamy interfejsy i nadklas:
                        for (Class<?> i : c.getInterfaces()) {
                            String i18nCode = findGetterFieldCode(i, field, false);
                            if (i18nCode != null) {
                                return i18nCode;
                            }
                        }
                        Class<?> s = c.getSuperclass();
                        if (s != null) {
                            return findGetterFieldCode(s, field, true);
                        }
                    }
                }
            }
        }
        if (reflectionCheck) {
            for (Class<?> i : c.getInterfaces()) {
                String i18nCode = findGetterFieldCode(i, field, false);
                if (i18nCode != null) {
                    return i18nCode;
                }
            }
        }

        return null;
    }
}

//   --------------------------------------------------------------------------
/**
 * 
 * 
 * @author Karol Kopacz
 * @since 1.0
 */
class SimpleMessageProvider implements IMessageProvider {
    @Override
    public String getMessage(String code, Object... args) {
        return getMessage(I18nUtils.getLocale(), code, args);
    }

    @Override
    public String getMessage(Locale locale, String code, Object... args) {
        return getMessageIfExists(locale, code, args);
    }

    @Override
    public String getMessageIfExists(String code, Object... args) {
        return getMessageIfExists(I18nUtils.getLocale(), code, args);
    }

    @Override
    public String getMessageIfExists(Locale locale, String code, Object... args) {
        if (locale == null) {
            locale = I18nUtils.getLocale();
        }

        if (args == null || args.length == 0) {
            return I18nUtils.getLocaleLanguageTag(locale) + " " + code + "[]";
        } else {
            StringBuilder builder = new StringBuilder();
            builder.append(code).append("[0: ");
            builder.append(String.valueOf(args[0]));
            for (int index = 1; index < args.length; index++) {
                builder.append(", ").append(index).append(": ");
                builder.append(String.valueOf(args[index]));
            }
            builder.append("]");
            return builder.toString();
        }
    }
}

//--------------------------------------------------------------------------
// Na podstawie kodu z projektu https://github.com/benjiman/benjiql
// /src/main/java/uk/co/benjiweber/benjiql/mocking
//--------------------------------------------------------------------------
class Recorder<T> {
    private T t;
    private RecordingObject recorder;

    public Recorder(T t, RecordingObject recorder) {
        this.t = t;
        this.recorder = recorder;
    }

    public String getCurrentPropertyName() {
        return recorder.getCurrentPropertyName();
    }

    public T getObject() {
        return t;
    }
}

class RecordingObject implements MethodInterceptor {
    private static final Map<Class<?>, Object> DEFAULT_VALUES = ImmutableMap.<Class<?>, Object>builder()
            .put(String.class, "string").put(Integer.class, 0).put(Float.class, 0f).put(Double.class, 0d)
            .put(Long.class, 0L).put(Character.class, 'c').put(Byte.class, (byte) 0).put(int.class, 0)
            .put(float.class, 0f).put(double.class, 0d).put(long.class, 0L).put(char.class, 'c')
            .put(byte.class, (byte) 0).build();

    private String currentPropertyName = "";
    private Recorder<?> currentMock = null;

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static <T> Recorder<T> create(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        final RecordingObject recordingObject = new RecordingObject();

        enhancer.setCallback(recordingObject);
        return new Recorder((T) enhancer.create(), recordingObject);
    }

    public Object intercept(Object o, Method method, Object[] os, MethodProxy mp) throws Throwable {
        if (method.getName().equals("getCurrentPropertyName")) {
            return getCurrentPropertyName();
        }
        currentPropertyName = method.getName();
        try {
            currentMock = create(method.getReturnType());
            return currentMock.getObject();
        } catch (IllegalArgumentException e) {
            return DEFAULT_VALUES.get((method.getReturnType()));
        }
    }

    public String getCurrentPropertyName() {
        return currentPropertyName;
    }
}