001/*
002 * Copyright 2022-2026 Revetware LLC.
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 * http://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 */
016
017package com.soklet.converter;
018
019import org.jspecify.annotations.NonNull;
020import org.jspecify.annotations.Nullable;
021
022import javax.annotation.concurrent.NotThreadSafe;
023import java.lang.reflect.Type;
024import java.util.Optional;
025
026import static java.util.Objects.requireNonNull;
027
028/**
029 * Thrown if an error occurs during value conversion.
030 * <p>
031 * For example, a {@link ValueConverter} for 'from' type {@link String} and 'to' type {@link Integer}
032 * might throw this exception if the 'from' value is {@code "abc"}.
033 *
034 * @author <a href="https://www.revetkn.com">Mark Allen</a>
035 */
036@NotThreadSafe
037public class ValueConversionException extends Exception {
038        @NonNull
039        private final Type fromType;
040        @Nullable
041        private final Object fromValue;
042        @NonNull
043        private final Type toType;
044
045        /**
046         * Creates an exception that describes the value conversion error.
047         *
048         * @param message   a message describing the error
049         * @param fromType  the 'from' type
050         * @param fromValue the value supplied for the 'from' type
051         * @param toType    the 'to' type
052         */
053        public ValueConversionException(@Nullable String message,
054                                                                                                                                        @NonNull Type fromType,
055                                                                                                                                        @Nullable Object fromValue,
056                                                                                                                                        @NonNull Type toType) {
057                super(message);
058
059                requireNonNull(fromType);
060                requireNonNull(toType);
061
062                this.fromType = fromType;
063                this.fromValue = fromValue;
064                this.toType = toType;
065        }
066
067        /**
068         * Creates an exception that describes the value conversion error.
069         *
070         * @param cause     the underlying exception that caused this one to be thrown
071         * @param fromType  the 'from' type
072         * @param fromValue the value supplied for the 'from' type
073         * @param toType    the 'to' type
074         */
075        public ValueConversionException(@Nullable Throwable cause,
076                                                                                                                                        @NonNull Type fromType,
077                                                                                                                                        @Nullable Object fromValue,
078                                                                                                                                        @NonNull Type toType) {
079                super(cause);
080
081                requireNonNull(fromType);
082                requireNonNull(toType);
083
084                this.fromType = fromType;
085                this.fromValue = fromValue;
086                this.toType = toType;
087        }
088
089        /**
090         * Creates an exception that describes the value conversion error.
091         *
092         * @param message   a message describing the error
093         * @param cause     the underlying exception that caused this one to be thrown
094         * @param fromType  the 'from' type
095         * @param fromValue the value supplied for the 'from' type
096         * @param toType    the 'to' type
097         */
098        public ValueConversionException(@Nullable String message,
099                                                                                                                                        @Nullable Throwable cause,
100                                                                                                                                        @NonNull Type fromType,
101                                                                                                                                        @Nullable Object fromValue,
102                                                                                                                                        @NonNull Type toType) {
103                super(message, cause);
104
105                requireNonNull(fromType);
106                requireNonNull(toType);
107
108                this.fromType = fromType;
109                this.fromValue = fromValue;
110                this.toType = toType;
111        }
112
113        /**
114         * The 'from' type of the failed {@link ValueConverter}.
115         *
116         * @return the 'from' type
117         */
118        @NonNull
119        public Type getFromType() {
120                return this.fromType;
121        }
122
123        /**
124         * The 'from' value of the failed {@link ValueConverter}.
125         *
126         * @return the 'from' value that could not be converted to the 'to' type
127         */
128        @NonNull
129        public Optional<Object> getFromValue() {
130                return Optional.ofNullable(this.fromValue);
131        }
132
133        /**
134         * The 'to' type of the failed {@link ValueConverter}.
135         *
136         * @return the 'to' type
137         */
138        @NonNull
139        public Type getToType() {
140                return this.toType;
141        }
142}