org.fastmongo.odm.dbobject.mapping.support.classname.ReflectionClassNameResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.fastmongo.odm.dbobject.mapping.support.classname.ReflectionClassNameResolver.java

Source

/*
 * Copyright (c) 2014 Alexander Gulko <kirhog at gmail dot com>.
 *
 * 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.fastmongo.odm.dbobject.mapping.support.classname;

import com.mongodb.DBObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.reflections.Reflections;

import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static org.fastmongo.odm.dbobject.mapping.core.ConverterHelper.CLASS_KEY;
import static org.fastmongo.odm.dbobject.mapping.core.ConverterHelper.restoreClassName;

/**
 * Implementation of classes names resolver interface using reflections.
 * Uses as additional condition to get classes names.
 *
 * @author Alexander Gulko
 */
public class ReflectionClassNameResolver implements ClassNameResolver {
    private static final Map<Class<?>, String> IMPLEMENTATION_BY_INTERFACE = new ConcurrentHashMap<>();
    private static final String NO_IMPLEMENTATION = "";

    private static final Map<Type, ResolveStrategy> STRATEGY_BY_CLASS = new ConcurrentHashMap<>();

    private List<ResolveStrategy> resolveStrategies;

    private Reflections reflections;

    public ReflectionClassNameResolver(String basePackage, List<ResolveStrategy> resolveStrategies) {
        this.resolveStrategies = resolveStrategies;
        reflections = new Reflections(basePackage, new SubTypesScanner());
    }

    @Override
    @SuppressWarnings("unchecked")
    public String getClassName(Type type, DBObject dbObject, String classPrefix) {
        if (dbObject.containsField(CLASS_KEY)) {
            return restoreClassName((String) dbObject.get(CLASS_KEY), classPrefix);
        }

        Class clazz = (Class) type;

        String className = IMPLEMENTATION_BY_INTERFACE.get(clazz);
        if (className == null) {
            Set<Class<?>> subTypes = reflections.getSubTypesOf(clazz);
            if (subTypes.size() == 1) {
                className = subTypes.iterator().next().getName();
            } else {
                className = NO_IMPLEMENTATION;
            }
            IMPLEMENTATION_BY_INTERFACE.put(clazz, className);
        }

        if (!NO_IMPLEMENTATION.equals(className)) {
            return className;
        }

        ResolveStrategy resolveStrategy = findSuitableStrategy(clazz);
        if (resolveStrategy != null) {
            className = resolveStrategy.getClassName(clazz, dbObject);
            className = className.startsWith(classPrefix) ? className : restoreClassName(className, classPrefix);
        }

        if (StringUtils.isEmpty(className)) {
            if (!clazz.isInterface()) {
                return clazz.getName();
            }

            throw new IllegalArgumentException(String.format(
                    "Can't find appropriate class name for '%s' and DBObject('%s') with classNamePrefix = %s", type,
                    StringUtils.abbreviateMiddle(dbObject.toString(), "<...>", 100), classPrefix));
        }

        return className;
    }

    @Override
    public boolean isWriteClassName(Type type) {
        ResolveStrategy resolveStrategy = findSuitableStrategy(type);
        return resolveStrategy != null && resolveStrategy.isWriteClassName(type);
    }

    @Override
    public boolean isWriteClassNameToLinks(Type type) {
        ResolveStrategy resolveStrategy = findSuitableStrategy(type);
        return resolveStrategy != null && resolveStrategy.isWriteClassNameToLinks(type);
    }

    private ResolveStrategy findSuitableStrategy(Type type) {
        if (!CollectionUtils.isEmpty(resolveStrategies)) {
            ResolveStrategy strategy = STRATEGY_BY_CLASS.get(type);
            if (strategy == null) {
                for (ResolveStrategy resolveStrategy : resolveStrategies) {
                    if (resolveStrategy.isSuitableStrategy(type)) {
                        strategy = resolveStrategy;
                        STRATEGY_BY_CLASS.put(type, strategy);
                        break;
                    }
                }
            }

            return strategy;
        }

        return null;
    }
}