Package c10n.share.utils

Source Code of c10n.share.utils.ReflectionUtils

/*
* Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you 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 c10n.share.utils;

import c10n.C10NKey;

import java.lang.reflect.Method;
import java.util.*;

public final class ReflectionUtils {
    private static final String KEY_DELIM = ".";

    /**
     * <p>Work out method's bundle key.</p>
     * <p/>
     * <h2>Bundle key resolution</h2>
     * <p>Bundle key is generated as follows:
     * <ul>
     * <li>If there are no {@link C10NKey} annotations, key is the <code>Class FQDN '.' Method Name</code>.
     * If method has arguments, method name is post-fixed with argument types delimited
     * with '_', e.g. <code>myMethod_String_int</code></li>
     * <p/>
     * <li>If declaring interface or any of the super-interfaces contain {@link C10NKey}
     * annotation <code>C</code> then
     * <ul>
     * <li>For methods without {@link C10NKey} annotation, key becomes <code>C '.' Method Name</code></li>
     * <li>For methods with {@link C10NKey} annotation <code>M</code>, key is <code>C '.' M</code></li>
     * <li>For methods with {@link C10NKey} annotation <code>M</code>, value for which starts with
     * a '.', the key is just <code>M</code> (i.e. key is assumed to be absolute)</li>
     * </ul>
     * </li>
     * <li>If no declaring interfaces have {@link C10NKey} annotation, but a method contains annotation
     * <code>M</code>, then key is just <code>M</code>.</li>
     * <li>Lastly, if global key prefix is specified, it is always prepended to the final key, delimited by '.'</li>
     * </ul>
     * </p>
     * <p/>
     * <h2>Looking for c10n key in parent interfaces</h2>
     * <p>The lookup of c10n key in parent interfaces is done breadth-first, starting from the declaring class.
     * That is, if the declaring class does not have c10n key, all interfaces it extends are checked in declaration
     * order first. If no key is found, this check is repeated for each of the super interfaces in the same order.
     * </p>
     *
     * @param keyPrefix global key prefix
     * @param method    method to extract the key from
     * @return method c10n bundle key (not null)
     */
    public static String getC10NKey(String keyPrefix, Method method) {
        String key = getKeyAnnotationBasedKey(method);
        if (null == key) {
            //fallback to default key based on class FQDN and method name
            key = ReflectionUtils.getDefaultKey(method);
        }
        if (keyPrefix.length() > 0) {
            key = keyPrefix + "." + key;
        }
        return key;
    }

    public static String getKeyAnnotationBasedKey(Method method) {
        String parentKey = findParentKey(method);
        String c10NKey = getKeyAnnotationValue(method);
        if (null == parentKey && null == c10NKey) {
            //C10NKey-based key is not available
            return null;
        }

        String methodKey;
        if (null != c10NKey) {
            methodKey = c10NKey;
            if (methodKey.startsWith(KEY_DELIM)) {
                //found an absolute key. Get rid of the
                //leading dot and look no further
                return methodKey.substring(1, methodKey.length());
            }
        } else {
            //method key based on methodName & arguments
            methodKey = getMethodKey(method);
        }
        if (parentKey != null) {
            return parentKey + KEY_DELIM + methodKey;
        }
        return methodKey;
    }

    /**
     * <p>Get the value provided with the {@link c10n.C10NKey} annotation. If annotation
     * is not declared returns <code>null</code></p>
     *
     * @param method method for which to retrieve the value {@link c10n.C10NKey} annotation
     * @return value of the declared annotation. <code>null</code> if not present.
     */
    public static String getKeyAnnotationValue(Method method) {
        C10NKey c10NKey = method.getAnnotation(C10NKey.class);
        if (null != c10NKey) {
            return c10NKey.value();
        }
        return null;
    }

    /**
     * <p>Works out the non-{@link C10NKey} bundle key for method, based on its
     * class FQDN and method name (plus argument types),
     * e.g. <code>com.myCompany.MyClass.myMethod_String_boolean</code></p>
     *
     * @param method the method for which to work out the key(not null)
     * @return method's default bundle key(not null)
     */
    public static String getDefaultKey(Method method) {
        StringBuilder sb = new StringBuilder();
        getDefaultKey(method, sb);
        return sb.toString();
    }

    public static void getDefaultKey(Method method, StringBuilder sb) {
        getFQNString(method.getDeclaringClass(), sb);
        sb.append(KEY_DELIM);
        getMethodKey(method, sb);
    }

    private static String getMethodKey(Method method) {
        StringBuilder sb = new StringBuilder();
        getMethodKey(method, sb);
        return sb.toString();
    }

    private static void getMethodKey(Method method, StringBuilder sb) {
        sb.append(method.getName());

        Class<?>[] params = method.getParameterTypes();
        if (params.length > 0) {
            sb.append('_');
            for (int i = 0; i < params.length; i++) {
                sb.append(params[i].getSimpleName());
                if (i + 1 < params.length) {
                    sb.append("_");
                }
            }
        }
    }

    public static String getFQNString(Class<?> clazz) {
        StringBuilder sb = new StringBuilder();
        getFQNString(clazz, sb);
        return sb.toString();
    }

    public static void getFQNString(Class<?> clazz, StringBuilder sb) {
        sb.append(clazz.getPackage().getName()).append(KEY_DELIM);
        getClassFQNString(clazz, sb);
    }

    private static void getClassFQNString(Class<?> clazz, StringBuilder sb) {
        Iterator<Class<?>> it = typeEnclosureHierarchy(clazz).descendingIterator();
        while (it.hasNext()) {
            sb.append(it.next().getSimpleName());
            if (it.hasNext()) {
                sb.append(KEY_DELIM);
            }
        }
    }

    private static String findParentKey(Method method) {
        for (Class<?> clazz : expandInterfaceHierarchy(method.getDeclaringClass())) {
            C10NKey key = clazz.getAnnotation(C10NKey.class);
            if (null != key) {
                return key.value();
            }
        }
        return null;
    }

    private static LinkedList<Class<?>> typeEnclosureHierarchy(Class<?> clazz) {
        LinkedList<Class<?>> typeHierarchy = new LinkedList<Class<?>>();
        do {
            typeHierarchy.add(clazz);
        } while ((clazz = clazz.getEnclosingClass()) != null);
        return typeHierarchy;
    }

    /*
     * Returns a list of super-interface breadth-first.
     */
    private static List<Class<?>> expandInterfaceHierarchy(Class<?> clazz) {
        List<Class<?>> res = new ArrayList<Class<?>>();
        res.add(clazz);
        expandInterfaceHierarchy(clazz, res);
        return res;
    }

    private static void expandInterfaceHierarchy(Class<?> clazz, List<Class<?>> res) {
        Class<?>[] intfs = clazz.getInterfaces();
        if (null != intfs) {
            Collections.addAll(res, intfs);
            for (Class<?> intf : intfs) {
                expandInterfaceHierarchy(intf, res);
            }
        }
    }
}
TOP

Related Classes of c10n.share.utils.ReflectionUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.