001package gudusoft.gsqlparser.sqlenv;
002
003import java.text.Collator;
004import java.util.Locale;
005import java.util.concurrent.ConcurrentHashMap;
006
007/**
008 * Collator 提供者(SQL Server COLLATION_BASED 专用)
009 *
010 * <p><strong>关键设计:</strong>
011 * <ul>
012 * <li>{@link Collator} 非线程安全,使用 {@link ThreadLocal} 缓存
013 * <li>按 collation 名称缓存实例(每个 collation 一个 ThreadLocal)
014 * <li>支持常见 SQL Server collation(CI_AS, CS_AS 等)
015 * </ul>
016 *
017 * <p>使用示例:
018 * <pre>
019 * CollatorProvider provider = new CollatorProvider();
020 *
021 * // 获取 Collator(自动 ThreadLocal 缓存)
022 * Collator collator = provider.getCollator("SQL_Latin1_General_CP1_CI_AS");
023 *
024 * // 比较标识符
025 * int result = collator.compare("MyTable", "MYTABLE");
026 * // CI (Case Insensitive): result == 0
027 * // CS (Case Sensitive): result != 0
028 * </pre>
029 *
030 * <p><strong>性能特性:</strong>
031 * <ul>
032 * <li>首次访问:创建 Collator 实例(~100μs)
033 * <li>后续访问:ThreadLocal.get()(~10ns)
034 * <li>内存开销:每线程每 collation 一个实例(~1KB)
035 * </ul>
036 *
037 * @since 3.1.0.9
038 */
039public class CollatorProvider {
040
041    // Per-collation 的 ThreadLocal 缓存
042    // Key: collation 名称(如 "SQL_Latin1_General_CP1_CI_AS")
043    // Value: ThreadLocal<Collator>(每线程独立实例)
044    private final ConcurrentHashMap<String, ThreadLocal<Collator>> collatorCache =
045        new ConcurrentHashMap<>();
046
047    // ===== 公共接口 =====
048
049    /**
050     * 获取 Collator 实例(ThreadLocal 缓存)
051     *
052     * <p>此方法线程安全,每个线程获得独立的 Collator 实例。
053     *
054     * @param collationName SQL Server collation 名称(例如:SQL_Latin1_General_CP1_CI_AS)
055     * @return Collator 实例(线程本地)
056     */
057    public Collator getCollator(String collationName) {
058        if (collationName == null || collationName.isEmpty()) {
059            // 默认:当前 Locale 的 Collator
060            return Collator.getInstance();
061        }
062
063        // 为每个 collation 名称创建一个 ThreadLocal
064        ThreadLocal<Collator> tl = collatorCache.computeIfAbsent(collationName, key ->
065            ThreadLocal.withInitial(() -> createCollator(key))
066        );
067
068        return tl.get();
069    }
070
071    /**
072     * 清理缓存(用于测试或内存管理)
073     *
074     * <p>警告:调用此方法后,所有线程的 Collator 实例将失效,下次访问时重新创建。
075     */
076    public void clearCache() {
077        collatorCache.clear();
078    }
079
080    // ===== 内部实现 =====
081
082    /**
083     * 根据 SQL Server collation 名称创建 Collator
084     *
085     * <p>SQL Server collation 名称格式:
086     * <pre>
087     * {Language}_{Region}_CP{CodePage}_{CaseSensitivity}_{AccentSensitivity}
088     *
089     * 例如:
090     * - SQL_Latin1_General_CP1_CI_AS  (Case Insensitive, Accent Sensitive)
091     * - SQL_Latin1_General_CP1_CS_AS  (Case Sensitive, Accent Sensitive)
092     * - Chinese_PRC_CI_AS             (简体中文, Case Insensitive)
093     * </pre>
094     *
095     * @param collationName SQL Server collation 名称
096     * @return 配置好的 Collator 实例
097     */
098    private Collator createCollator(String collationName) {
099        // 解析 collation 名称
100        Locale locale = parseLocale(collationName);
101        Collator collator = Collator.getInstance(locale);
102
103        // 设置强度(Strength)
104        if (collationName.contains("_CI_") || collationName.contains("_CI")) {
105            // Case Insensitive(CI)
106            collator.setStrength(Collator.SECONDARY);  // 忽略大小写
107        } else if (collationName.contains("_CS_") || collationName.contains("_CS")) {
108            // Case Sensitive(CS)
109            collator.setStrength(Collator.TERTIARY);   // 区分大小写
110        } else {
111            // 默认:根据 Locale 决定(通常是 TERTIARY)
112            collator.setStrength(Collator.TERTIARY);
113        }
114
115        // 设置分解模式(Decomposition)
116        if (collationName.contains("_AS")) {
117            // Accent Sensitive(AS)
118            collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
119        } else if (collationName.contains("_AI")) {
120            // Accent Insensitive(AI)
121            collator.setDecomposition(Collator.NO_DECOMPOSITION);
122        } else {
123            // 默认:Canonical Decomposition
124            collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
125        }
126
127        return collator;
128    }
129
130    /**
131     * 从 collation 名称解析 Locale
132     *
133     * <p>这是简化实现,仅支持常见 collation。
134     * 生产环境需要完整的 SQL Server collation → Java Locale 映射表。
135     *
136     * @param collationName SQL Server collation 名称
137     * @return 对应的 Java Locale
138     */
139    private Locale parseLocale(String collationName) {
140        String lowerName = collationName.toLowerCase();
141
142        // SQL Server 常见 collation 映射
143        if (lowerName.contains("latin1") || lowerName.contains("sql_")) {
144            // Latin1 / SQL_ 前缀 → 英语(美国)
145            return new Locale("en", "US");
146        } else if (lowerName.contains("chinese_prc") || lowerName.contains("chinese_simplified")) {
147            // 简体中文
148            return new Locale("zh", "CN");
149        } else if (lowerName.contains("chinese_taiwan") || lowerName.contains("chinese_traditional")) {
150            // 繁体中文(台湾)
151            return new Locale("zh", "TW");
152        } else if (lowerName.contains("chinese_hong_kong")) {
153            // 繁体中文(香港)
154            return new Locale("zh", "HK");
155        } else if (lowerName.contains("japanese")) {
156            // 日语
157            return new Locale("ja", "JP");
158        } else if (lowerName.contains("korean")) {
159            // 韩语
160            return new Locale("ko", "KR");
161        } else if (lowerName.contains("arabic")) {
162            // 阿拉伯语
163            return new Locale("ar");
164        } else if (lowerName.contains("french")) {
165            // 法语
166            return new Locale("fr", "FR");
167        } else if (lowerName.contains("german")) {
168            // 德语
169            return new Locale("de", "DE");
170        } else if (lowerName.contains("spanish")) {
171            // 西班牙语
172            return new Locale("es", "ES");
173        } else if (lowerName.contains("italian")) {
174            // 意大利语
175            return new Locale("it", "IT");
176        } else if (lowerName.contains("russian")) {
177            // 俄语
178            return new Locale("ru", "RU");
179        } else {
180            // 默认:英语(美国)
181            return Locale.US;
182        }
183
184        // 生产环境实现:
185        // 1. 维护完整的 SQL Server collation → Locale 映射表
186        // 2. 解析 collation 名称的各个组成部分
187        // 3. 根据 Language 和 Region 构造 Locale
188        // 4. 参考:https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support
189    }
190
191    // ===== 调试方法 =====
192
193    /**
194     * 获取缓存大小(不同 collation 的数量)
195     *
196     * @return 缓存的 collation 数量
197     */
198    public int getCacheSize() {
199        return collatorCache.size();
200    }
201
202    /**
203     * 测试 collation 比较(用于调试)
204     *
205     * @param collationName collation 名称
206     * @param str1 字符串 1
207     * @param str2 字符串 2
208     * @return 比较结果(0: 相等, <0: str1 < str2, >0: str1 > str2)
209     */
210    public int testCompare(String collationName, String str1, String str2) {
211        Collator collator = getCollator(collationName);
212        return collator.compare(str1, str2);
213    }
214}