001/*
002 * Copyright 2015 Transmogrify 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.ThreadSafe;
023import java.lang.reflect.ParameterizedType;
024import java.lang.reflect.Type;
025import java.util.Objects;
026
027import static java.lang.String.format;
028
029/**
030 * Construct for creating type tokens that represent generic types.
031 * <p>
032 * In Java, you may express a type token for a non-generic type like {@code String.class}. But you cannot say
033 * <code>List&lt;String&gt;.class</code>. Using {@code TypeReference}, you can express the latter as follows:
034 * <p>
035 * <code>new TypeReference&lt;List&lt;String&gt;&gt;() &#123;&#125;</code>
036 * <p>
037 * See <a
038 * href="http://gafter.blogspot.com/2006/12/super-type-tokens.html">http://gafter.blogspot.com/2006/12/super-type-
039 * tokens.html</a> for more details.
040 *
041 * @author Neal Gafter
042 * @author Bob Lee
043 * @author <a href="https://www.revetkn.com">Mark Allen</a>
044 */
045@ThreadSafe
046public abstract class TypeReference<T> {
047        @NonNull
048        private final Type type;
049
050        protected TypeReference() {
051                Type superclass = getClass().getGenericSuperclass();
052
053                if (superclass instanceof Class)
054                        throw new IllegalStateException("Missing type parameter.");
055
056                this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
057        }
058
059        @Override
060        @NonNull
061        public String toString() {
062                return format("%s{type=%s}", getClass().getSimpleName(), getType());
063        }
064
065        @Override
066        public boolean equals(@Nullable Object object) {
067                if (this == object)
068                        return true;
069
070                if (!(object instanceof TypeReference<?> typeReference))
071                        return false;
072
073                return Objects.equals(getType(), typeReference.getType());
074        }
075
076        @Override
077        public int hashCode() {
078                return Objects.hash(getType());
079        }
080
081        @NonNull
082        public Type getType() {
083                return this.type;
084        }
085}