001/*
002 *    Copyright 2009-2023 the original author or authors.
003 *
004 *    Licensed under the Apache License, Version 2.0 (the "License");
005 *    you may not use this file except in compliance with the License.
006 *    You may obtain a copy of the License at
007 *
008 *       https://www.apache.org/licenses/LICENSE-2.0
009 *
010 *    Unless required by applicable law or agreed to in writing, software
011 *    distributed under the License is distributed on an "AS IS" BASIS,
012 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *    See the License for the specific language governing permissions and
014 *    limitations under the License.
015 */
016package com.mybatisflex.core.mybatis.binding;
017
018import com.mybatisflex.core.mybatis.FlexConfiguration;
019import org.apache.ibatis.binding.BindingException;
020import org.apache.ibatis.binding.MapperRegistry;
021import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
022import org.apache.ibatis.io.ResolverUtil;
023import org.apache.ibatis.session.SqlSession;
024
025import java.util.Collection;
026import java.util.Collections;
027import java.util.Map;
028import java.util.Set;
029import java.util.concurrent.ConcurrentHashMap;
030
031public class FlexMapperRegistry extends MapperRegistry {
032    private final FlexConfiguration config;
033    private final Map<Class<?>, FlexMapperProxyFactory<?>> knownMappers = new ConcurrentHashMap<>();
034
035    public FlexMapperRegistry(FlexConfiguration config) {
036        super(config);
037        this.config = config;
038    }
039
040    @SuppressWarnings("unchecked")
041    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
042        final FlexMapperProxyFactory<T> mapperProxyFactory = (FlexMapperProxyFactory<T>) knownMappers.get(type);
043        if (mapperProxyFactory == null) {
044            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
045        }
046        try {
047            return mapperProxyFactory.newInstance(sqlSession, config);
048        } catch (Exception e) {
049            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
050        }
051    }
052
053    public <T> boolean hasMapper(Class<T> type) {
054        return knownMappers.containsKey(type);
055    }
056
057    public <T> void addMapper(Class<T> type) {
058        if (type.isInterface()) {
059            if (hasMapper(type)) {
060                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
061            }
062            boolean loadCompleted = false;
063            try {
064                knownMappers.put(type, new FlexMapperProxyFactory<>(type));
065                // It's important that the type is added before the parser is run
066                // otherwise the binding may automatically be attempted by the
067                // mapper parser. If the type is already known, it won't try.
068                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
069                parser.parse();
070                loadCompleted = true;
071            } finally {
072                if (!loadCompleted) {
073                    knownMappers.remove(type);
074                }
075            }
076        }
077    }
078
079    /**
080     * Gets the mappers.
081     *
082     * @return the mappers
083     * @since 3.2.2
084     */
085    public Collection<Class<?>> getMappers() {
086        return Collections.unmodifiableCollection(knownMappers.keySet());
087    }
088
089    /**
090     * Adds the mappers.
091     *
092     * @param packageName the package name
093     * @param superType   the super type
094     * @since 3.2.2
095     */
096    public void addMappers(String packageName, Class<?> superType) {
097        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
098        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
099        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
100        for (Class<?> mapperClass : mapperSet) {
101            addMapper(mapperClass);
102        }
103    }
104
105    /**
106     * Adds the mappers.
107     *
108     * @param packageName the package name
109     * @since 3.2.2
110     */
111    public void addMappers(String packageName) {
112        addMappers(packageName, Object.class);
113    }
114}