com.github.erchu.beancp.MapperBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.github.erchu.beancp.MapperBuilder.java

Source

/*
 * bean-cp
 * Copyright (c) 2014, Rafal Chojnacki, All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */
package com.github.erchu.beancp;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.Validate.*;

/**
 * Builds mapper implementation. This class is not thread-safe.
 */
public final class MapperBuilder implements MappingInfo {

    private final List<DeclarativeMapImpl<?, ?>> _maps = new LinkedList<>();

    private final List<Converter<?, ?>> _converters = new LinkedList<>();

    private final List<MapConventionExecutor> _mapAnyConventions = new LinkedList<>();

    private boolean _mapperBuilded = false;

    /**
     * Adds new mapping defined by map. Both {@code source} and {@code destination} classes must:
     * <ul>
     * <li>Must have default (no argument) public or protected constructor. This
     * requirement is valid even if destination object builder is provided by
     * {@link DeclarativeMap#constructDestinationObjectUsing(java.util.function.Supplier)}
     * method.</li>
     * <li>Cannot be final</li>
     * <li>Cannot be inner non-static classes.</li>
     * </ul>
     *
     * @param <S> source object class.
     * @param <D> destination object class.
     * @param sourceClass source object class.
     * @param destinationClass destination object class.
     * @param mapConfiguration map configuration.
     *
     * @return this (for method chaining)
     */
    public <S, D> MapperBuilder addMap(final Class<S> sourceClass, final Class<D> destinationClass,
            final DeclarativeMapSetup<S, D> mapConfiguration) throws MapperConfigurationException {
        validateAddMappingAction(sourceClass, destinationClass);

        DeclarativeMapImpl map = new DeclarativeMapImpl(sourceClass, destinationClass, mapConfiguration);
        map.configure(this);

        _maps.add(map);

        return this;
    }

    /**
     * Adds new mappings implemented by converter.
     *
     * @param <S> source object class.
     * @param <D> destination object class.
     * @param sourceClass source object class.
     * @param destinationClass destination object class.
     * @param convertionAction converter action, must be thread-safe.
     *
     * @return this (for method chaining)
     */
    public <S, D> MapperBuilder addConverter(final Class<S> sourceClass, final Class<D> destinationClass,
            final Function<S, D> convertionAction) throws MapperConfigurationException {
        validateAddMappingAction(sourceClass, destinationClass);

        _converters.add(new Converter<>(sourceClass, destinationClass, convertionAction));

        return this;
    }

    /**
     * Adds new mapping implemented by converter.
     *
     * @param <S> source object class.
     * @param <D> destination object class.
     * @param sourceClass source object class.
     * @param destinationClass destination object class.
     * @param convertionAction converter action, must be thread-safe.
     *
     * @return this (for method chaining)
     */
    public <S, D> MapperBuilder addConverter(final Class<S> sourceClass, final Class<D> destinationClass,
            final BiFunction<Mapper, S, D> convertionAction) throws MapperConfigurationException {
        validateAddMappingAction(sourceClass, destinationClass);

        _converters.add(new Converter<>(sourceClass, destinationClass, convertionAction));

        return this;
    }

    /**
     * Adds new mappings implemented by converters.
     *
     * @param converters converters to add.
     *
     * @return this (for method chaining)
     */
    public MapperBuilder addConverter(final Converter<?, ?>... converters) throws MapperConfigurationException {

        for (Converter<?, ?> i : converters) {
            validateAddMappingAction(i.getSourceClass(), i.getDestinationClass());
        }

        this._converters.addAll(Arrays.asList(converters));

        return this;
    }

    /**
     * If two data types has no mapping defined by
     * {@link #addMap(java.lang.Class, java.lang.Class, com.github.erchu.beancp.DeclarativeMapSetup)}
     * or any of {@code addConverter} methods then this convention will be used.
     *
     * @param conventions convention to add.
     *
     * @return this (for method chaining)
     */
    public MapperBuilder addMapAnyByConvention(final MapConvention... conventions)
            throws MapperConfigurationException {
        List<MapConventionExecutor> conventionExecutors = Arrays.stream(conventions)
                .map(i -> new MapConventionExecutor(i)).collect(Collectors.toList());

        this._mapAnyConventions.addAll(conventionExecutors);

        return this;
    }

    /**
     * Creates map implementation from definitions. After executed no other methods can be executed
     * on this instance.
     *
     * @return map implementation.
     */
    public Mapper buildMapper() {
        this._mapperBuilded = true;

        return new MapperImpl(_converters, _maps, _mapAnyConventions);
    }

    @Override
    public boolean isMapAvailable(final Class sourceClass, final Class destinationClass) {
        notNull(sourceClass, "sourceClass");
        notNull(destinationClass, "destinationClass");

        return MapperExecutorSelector.isMapAvailable(this, sourceClass, destinationClass,
                Collections.unmodifiableCollection(_maps), Collections.unmodifiableCollection(_mapAnyConventions));
    }

    @Override
    public boolean isConverterAvailable(final Class sourceClass, final Class destinationClass) {
        notNull(sourceClass, "sourceClass");
        notNull(destinationClass, "destinationClass");

        return MapperExecutorSelector.isConverterAvailable(this, sourceClass, destinationClass,
                Collections.unmodifiableCollection(_converters));
    }

    private <S, D> void validateAddMappingAction(final Class<S> sourceClass, final Class<D> destinationClass) {
        notNull(sourceClass, "sourceClass");
        notNull(destinationClass, "destinationClass");

        if (this._mapperBuilded) {
            throw new MapperConfigurationException("Mapper already builded. No changes allowed.");
        }

        for (DeclarativeMapImpl<?, ?> i : _maps) {
            if (i.getSourceClass().equals(sourceClass) && i.getDestinationClass().equals(destinationClass)) {
                throw new MapperConfigurationException(String.format("Mapping from %s to %s already defined.",
                        sourceClass.getName(), destinationClass.getName()));
            }
        }
    }
}