All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.dbclient.DbMapperManagerImpl Maven / Gradle / Ivy

There is a newer version: 4.1.4
Show newest version
/*
 * Copyright (c) 2019, 2021 Oracle and/or its affiliates.
 *
 * 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 io.helidon.dbclient;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

import io.helidon.common.GenericType;
import io.helidon.common.mapper.MapperException;
import io.helidon.dbclient.spi.DbMapperProvider;

/**
 * Default implementation of the DbMapperManager.
 */
class DbMapperManagerImpl implements DbMapperManager {
    public static final String ERROR_NO_MAPPER_FOUND = "Failed to find DB mapper.";
    private final List providers;
    private final Map, DbMapper> byClass = new ConcurrentHashMap<>();
    private final Map, DbMapper> byType = new ConcurrentHashMap<>();

    DbMapperManagerImpl(Builder builder) {
        this.providers = builder.mapperProviders();
    }

    @Override
    public  T read(DbRow row, Class expectedType) {
        return executeMapping(() -> findMapper(expectedType, false)
                                      .read(row),
                              row,
                              TYPE_DB_ROW,
                              GenericType.create(expectedType));
    }

    @Override
    public  T read(DbRow row, GenericType expectedType) {
        return executeMapping(() -> findMapper(expectedType, false)
                                      .read(row),
                              row,
                              TYPE_DB_ROW,
                              expectedType);
    }

    @Override
    public  Map toNamedParameters(T value, Class valueClass) {
        return executeMapping(() -> findMapper(valueClass, false)
                                      .toNamedParameters(value),
                              value,
                              GenericType.create(valueClass),
                              TYPE_NAMED_PARAMS);
    }

    @Override
    public  List toIndexedParameters(T value, Class valueClass) {
        return executeMapping(() -> findMapper(valueClass, false)
                                      .toIndexedParameters(value),
                              value,
                              GenericType.create(valueClass),
                              TYPE_INDEXED_PARAMS);
    }

    private  T executeMapping(Supplier mapping, Object source, GenericType sourceType, GenericType targetType) {
        try {
            return mapping.get();
        } catch (MapperException e) {
            throw e;
        } catch (Exception e) {
            throw createMapperException(source, sourceType, targetType, e);
        }
    }

    @SuppressWarnings("unchecked")
    private  DbMapper findMapper(Class type, boolean fromTypes) {
        DbMapper mapper = byClass.computeIfAbsent(type, aClass -> {
            return fromProviders(type)
                    .orElseGet(() -> {
                        GenericType targetType = GenericType.create(type);
                        if (fromTypes) {
                            return notFoundMapper(targetType);
                        }
                        return findMapper(targetType, true);
                    });
        });

        return (DbMapper) mapper;
    }

    @SuppressWarnings("unchecked")
    private  DbMapper findMapper(GenericType type, boolean fromClasses) {
        DbMapper mapper = byType.computeIfAbsent(type, aType -> {
            return fromProviders(type)
                    .orElseGet(() -> {
                        if (!fromClasses && type.isClass()) {
                            return findMapper((Class) type.rawType(), true);
                        }
                        return notFoundMapper(type);
                    });
        });

        return (DbMapper) mapper;
    }

    private  Optional> fromProviders(Class type) {
        return providers.stream()
                .flatMap(provider -> provider.mapper(type).stream()).findFirst();
    }

    private  Optional> fromProviders(GenericType type) {
        return providers.stream()
                .flatMap(provider -> provider.mapper(type).stream()).findFirst();
    }

    private RuntimeException createMapperException(Object source,
                                                   GenericType sourceType,
                                                   GenericType targetType,
                                                   Throwable throwable) {

        throw new MapperException(sourceType,
                                  targetType,
                                  "Failed to map source of class '" + source.getClass().getName() + "'",
                                  throwable);
    }

    private static  DbMapper notFoundMapper(GenericType type) {
        return new DbMapper() {
            @Override
            public T read(DbRow row) {
                throw new MapperException(TYPE_DB_ROW, type, ERROR_NO_MAPPER_FOUND);
            }

            @Override
            public Map toNamedParameters(T value) {
                throw new MapperException(type, TYPE_NAMED_PARAMS, ERROR_NO_MAPPER_FOUND);
            }

            @Override
            public List toIndexedParameters(T value) {
                throw new MapperException(type, TYPE_INDEXED_PARAMS, ERROR_NO_MAPPER_FOUND);
            }
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy