com.haulmont.cuba.core.config.type.TypeFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.core.config.type.TypeFactory.java

Source

/*
 * A High-Level Framework for Application Configuration
 *
 * Copyright 2007 Merlin Hughes / Learning Objects, Inc.
 *
 * 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 com.haulmont.cuba.core.config.type;

import com.haulmont.chile.core.datatypes.impl.EnumClass;
import com.haulmont.cuba.core.config.ConfigUtil;
import com.haulmont.cuba.core.config.EnumStore;
import com.haulmont.cuba.core.config.EnumStoreMode;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans;
import org.apache.commons.lang.ClassUtils;

import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * A class that gets a configuration type by retrieving a string value
 * from the configuration and then building the appropriate type from
 * that string.
 */
public abstract class TypeFactory {
    public static final String ENTITY_FACTORY_BEAN_NAME = "cuba_ConfigEntityFactory";

    /**
     * Build an instance of the configuration type from a string.
     *
     * @param string The string value.
     * @return An instance of the type.
     */
    public abstract Object build(String string);

    /**
     * Standard static factory methods to look for.
     */
    private static final String[] FACTORY_METHOD_NAMES = { "valueOf", "getInstance", "parse", // Level
            "forName" // Class
    };

    /**
     * Get a TypeFactory instance appropriate for the return type of the
     * specified configuration interface method.
     *
     * @param configInterface The configuration interface.
     * @param method          The method.
     * @return An appropriate TypeFactory.
     * @throws IllegalArgumentException If the type is not supported.
     */
    public static TypeFactory getInstance(Class<?> configInterface, Method method) {
        Class<?> returnType = method.getReturnType();
        if (returnType.isPrimitive()) {
            returnType = ClassUtils.primitiveToWrapper(returnType);
        }
        Factory factory = ConfigUtil.getAnnotation(configInterface, method, Factory.class, true);
        if (factory != null) {
            try {
                if ("".equals(factory.method())) {
                    return factory.factory().newInstance();
                } else {
                    String methodName = factory.method();
                    Method factoryMethod = returnType.getMethod(methodName, String.class);
                    if (!isAcceptableMethod(returnType, factoryMethod)) {
                        throw new IllegalArgumentException("Invalid factory method: " + factoryMethod);
                    }
                    return new StaticTypeFactory(factoryMethod);
                }
            } catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) {
                throw new RuntimeException("Unable to instantiate an type factory", e);
            }
        } else {
            if (Entity.class.isAssignableFrom(returnType)) {
                return AppBeans.get(ENTITY_FACTORY_BEAN_NAME, TypeFactory.class);
            } else {
                if (EnumClass.class.isAssignableFrom(returnType)) {
                    EnumStore mode = ConfigUtil.getAnnotation(configInterface, method, EnumStore.class, true);
                    if (mode != null && EnumStoreMode.ID == mode.value()) {
                        @SuppressWarnings("unchecked")
                        Class<EnumClass> enumeration = (Class<EnumClass>) returnType;
                        Class<?> idType = ConfigUtil.getEnumIdType(enumeration);
                        TypeFactory idFactory = getInferred(idType);
                        try {
                            Method fromIdMethod = returnType.getMethod("fromId", idType);
                            if (!isAcceptableMethod(returnType, fromIdMethod) || idFactory == null) {
                                throw new IllegalArgumentException(
                                        "Cannot use method as factory method: " + method);
                            }
                            return new EnumClassFactory(idFactory, fromIdMethod);
                        } catch (NoSuchMethodException e) {
                            throw new IllegalArgumentException(
                                    "fromId method is not found for " + enumeration.getName());
                        }
                    }
                }
                TypeFactory factoryT = getInferred(returnType);
                if (factoryT == null) {
                    throw new IllegalArgumentException("Unsupported return type for " + method);
                }
                return factoryT;
            }
        }
    }

    @Nullable
    private static TypeFactory getInferred(Class<?> returnType) {
        // special case due to Character.valueOf(char) does not accept String
        if (returnType == Character.class) {
            return new CharTypeFactory();
        }

        for (String methodName : FACTORY_METHOD_NAMES) {
            try {
                Method factoryMethod = returnType.getMethod(methodName, String.class);
                if (isAcceptableMethod(returnType, factoryMethod)) {
                    return new StaticTypeFactory(factoryMethod);
                }
            } catch (NoSuchMethodException ex) {
                // Ignore failure.
            }
        }
        try {
            Constructor ctor = returnType.getConstructor(String.class);
            if (Modifier.isPublic(ctor.getModifiers())) {
                return new ConstructorTypeFactory(ctor);
            }
        } catch (NoSuchMethodException e) {
            return null;
        }
        return null;
    }

    private static boolean isAcceptableMethod(Class<?> returnType, Method factoryMethod) {
        int modifiers = factoryMethod.getModifiers();
        return Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)
                && returnType.isAssignableFrom(factoryMethod.getReturnType());
    }
}