001package ball.util;
002/*-
003 * ##########################################################################
004 * Utilities
005 * %%
006 * Copyright (C) 2008 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import ball.lang.PrimitiveTypeMap;
022import java.util.TreeMap;
023
024import static java.util.Comparator.comparing;
025
026/**
027 * Conversion utility based on {@link Factory}.
028 *
029 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
030 */
031public class Converter extends TreeMap<Class<?>,Factory<?>> {
032    private static final long serialVersionUID = -3178874315076658917L;
033
034    private static final Converter INSTANCE = new Converter();
035
036    private Converter() {
037        super(comparing(Class::getName));
038
039        put(String.class, new Factory<>(String.class));
040
041        PrimitiveTypeMap.INSTANCE.values().stream()
042            .forEach(t -> put(t, new Factory<>(t)));
043        PrimitiveTypeMap.INSTANCE.keySet().stream()
044            .forEach(t -> put(t, get(PrimitiveTypeMap.INSTANCE.get(t))));
045    }
046
047    /**
048     * Static method to convert the argument to the specified type
049     * ({@link Class}).
050     *
051     * @param   from            The source value.
052     * @param   type            The target type ({@link Class}).
053     *
054     * @return  The converted value.
055     */
056    public static Object convertTo(Object from, Class<?> type) {
057        Object to = null;
058
059        try {
060            if (from == null || type.isAssignableFrom(from.getClass())) {
061                to = from;
062            } else {
063                to =
064                    INSTANCE.computeIfAbsent(type,
065                                             k -> (INSTANCE.values().stream()
066                                                   .filter(t -> k.isAssignableFrom(t.getType()))
067                                                   .findFirst()
068                                                   .orElse(new Factory<>(k))))
069                    .getInstance(from);
070            }
071        } catch (RuntimeException exception) {
072            throw exception;
073        } catch (Exception exception) {
074            throw new IllegalArgumentException(exception);
075        }
076
077        return to;
078    }
079}