001/*
002 *  Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com).
003 *  <p>
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 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
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 */
016package com.agentsflex.core.util;
017
018import org.jetbrains.annotations.NotNull;
019import org.jetbrains.annotations.Nullable;
020
021import java.lang.reflect.*;
022import java.math.BigDecimal;
023import java.math.BigInteger;
024import java.net.URI;
025import java.net.URL;
026import java.time.*;
027import java.util.*;
028import java.util.concurrent.ConcurrentHashMap;
029
030/**
031 * Java 类型到 JSON Schema 类型的映射工具类
032 * <p>
033 * 支持标准类型、集合类型、泛型类型及自定义类型注册
034 * <p>
035 * JSON Schema 标准类型: string, number, integer, boolean, object, array, null
036 *
037 * @author fuhai
038 * @since 2026/03/10
039 */
040public final class JsonSchemaTypeMapper {
041
042    /**
043     * 类型映射缓存:避免重复反射解析,提升性能
044     */
045    private static final Map<Class<?>, String> TYPE_CACHE = new ConcurrentHashMap<>(64);
046
047    /**
048     * 自定义类型映射策略(可扩展)
049     */
050    private static final Map<Class<?>, TypeMappingStrategy> CUSTOM_STRATEGIES = new ConcurrentHashMap<>(8);
051
052    /**
053     * 私有构造,防止实例化
054     */
055    private JsonSchemaTypeMapper() {
056    }
057
058    /**
059     * 将 Java Class 映射为 JSON Schema 类型字符串
060     *
061     * @param javaType Java 类型
062     * @return JSON Schema 类型: string/number/integer/boolean/object/array/null
063     */
064    @NotNull
065    public static String mapToSchemaType(@Nullable Class<?> javaType) {
066        if (javaType == null) {
067            return "string";
068        }
069
070        // 1. 优先检查自定义策略
071        TypeMappingStrategy custom = CUSTOM_STRATEGIES.get(javaType);
072        if (custom != null) {
073            return custom.mapType(javaType);
074        }
075
076        // 2. 缓存命中
077        return TYPE_CACHE.computeIfAbsent(javaType, JsonSchemaTypeMapper::doMapType);
078    }
079
080    /**
081     * 解析数组/集合元素的 JSON Schema 类型(支持泛型)
082     *
083     * @param genericType 泛型类型信息,如 {@code List<String>} 的 {@code String}
084     * @return 元素类型的 JSON Schema 类型
085     */
086    @NotNull
087    public static String resolveArrayItemType(@Nullable Type genericType) {
088        if (genericType == null) {
089            return "string";
090        }
091
092        // 1. ParameterizedType: List<String>, Map<K,V>
093        if (genericType instanceof ParameterizedType) {
094            Type[] args = ((ParameterizedType) genericType).getActualTypeArguments();
095            if (args.length > 0) {
096                return resolveType(args[0]);
097            }
098        }
099
100        // 2. GenericArrayType: T[], List<T>[]
101        if (genericType instanceof GenericArrayType) {
102            Type componentType = ((GenericArrayType) genericType).getGenericComponentType();
103            return resolveType(componentType);
104        }
105
106        // 3. WildcardType: List<? extends Number> → 取上界
107        if (genericType instanceof WildcardType) {
108            Type[] upperBounds = ((WildcardType) genericType).getUpperBounds();
109            if (upperBounds.length > 0) {
110                return resolveType(upperBounds[0]);
111            }
112            Type[] lowerBounds = ((WildcardType) genericType).getLowerBounds();
113            if (lowerBounds.length > 0) {
114                return resolveType(lowerBounds[0]);
115            }
116            return "object";
117        }
118
119        // 4. TypeVariable: List<T> → 默认 object(可从上下文进一步解析)
120        if (genericType instanceof TypeVariable) {
121            return "object";
122        }
123
124        // 5. 普通 Class
125        if (genericType instanceof Class) {
126            return mapToSchemaType((Class<?>) genericType);
127        }
128
129        // 6. 兜底:最宽兼容策略
130        return "object";
131    }
132
133    /**
134     * 注册自定义类型映射策略
135     * <p>
136     * 示例:{@code registerStrategy(LocalDateTime.class, t -> "string", "date-time")}
137     *
138     * @param type     Java 类型
139     * @param strategy 映射策略
140     */
141    public static void registerStrategy(@NotNull Class<?> type, @NotNull TypeMappingStrategy strategy) {
142        CUSTOM_STRATEGIES.put(type, strategy);
143        TYPE_CACHE.remove(type); // 清除缓存,确保新策略生效
144    }
145
146    /**
147     * 清除缓存(测试或热更新场景使用)
148     */
149    public static void clearCache() {
150        TYPE_CACHE.clear();
151    }
152
153    // =============== 内部映射逻辑 ===============
154
155    @NotNull
156    private static String doMapType(@NotNull Class<?> type) {
157        // 1. 数组类型(含多维)
158        if (type.isArray()) {
159            return "array";
160        }
161
162        // 2. Optional 类型:解包或返回 string(根据业务需求调整)
163        if (Optional.class.isAssignableFrom(type)) {
164            return "string";
165        }
166
167        // 3. Map → object
168        if (Map.class.isAssignableFrom(type)) {
169            return "object";
170        }
171
172        // 4. Collection → array
173        if (Collection.class.isAssignableFrom(type)) {
174            return "array";
175        }
176
177        // 5. 整数类型(使用类型层次判断,避免字符串匹配)
178        if (isIntegerNumericType(type)) {
179            return "integer";
180        }
181
182        // 6. 浮点类型
183        if (isFloatingNumericType(type)) {
184            return "number";
185        }
186
187        // 7. 布尔类型
188        if (boolean.class == type || Boolean.class == type) {
189            return "boolean";
190        }
191
192        // 8. 字符串及类字符串类型
193        if (isStringLikeType(type)) {
194            return "string";
195        }
196
197        // 9. 枚举类型
198        if (type.isEnum()) {
199            return "string";
200        }
201
202        // 10. 未知类型(实体类/自定义类):默认 object(符合 JSON Schema 规范)
203        // 大模型传参是 JSON 对象,Java 实体类应对应 object 类型
204        return "object";
205    }
206
207    @NotNull
208    private static String resolveType(@Nullable Type type) {
209        if (type instanceof Class) {
210            return mapToSchemaType((Class<?>) type);
211        } else if (type instanceof ParameterizedType) {
212            Class<?> rawType = (Class<?>) ((ParameterizedType) type).getRawType();
213            return mapToSchemaType(rawType);
214        } else {
215            // 递归处理其他 Type 子类型
216            return resolveArrayItemType(type);
217        }
218    }
219
220    // =============== 类型判断辅助方法 ===============
221
222    private static boolean isIntegerNumericType(Class<?> type) {
223        return type == int.class || type == Integer.class ||
224            type == long.class || type == Long.class ||
225            type == short.class || type == Short.class ||
226            type == byte.class || type == Byte.class ||
227            type == BigInteger.class;
228    }
229
230    private static boolean isFloatingNumericType(Class<?> type) {
231        return type == float.class || type == Float.class ||
232            type == double.class || type == Double.class ||
233            type == BigDecimal.class;
234    }
235
236    private static boolean isStringLikeType(Class<?> type) {
237        return type == String.class ||
238            type == char.class || type == Character.class ||
239            type == CharSequence.class ||
240            // 时间类型(大模型通常以字符串传输)
241            type == Date.class ||
242            type == java.sql.Date.class ||
243            type == java.sql.Timestamp.class ||
244            type == LocalDate.class ||
245            type == LocalDateTime.class ||
246            type == LocalTime.class ||
247            type == Instant.class ||
248            type == ZonedDateTime.class ||
249            type == OffsetDateTime.class ||
250            // 其他常见字符串语义类型
251            type == UUID.class ||
252            type == URI.class ||
253            type == URL.class;
254    }
255
256    /**
257     * 类型映射策略函数式接口(支持扩展)
258     */
259    @FunctionalInterface
260    public interface TypeMappingStrategy {
261        /**
262         * 将 Java 类型映射为 JSON Schema 类型
263         *
264         * @param javaType Java 类型
265         * @return JSON Schema 类型字符串
266         */
267        @NotNull
268        String mapType(@NotNull Class<?> javaType);
269    }
270}