public final class GspCaseData extends Object
All data comes from the committed binary resource gsp-case-data.bin,
generated from vendored UCD "16.0.0" files. Nothing here consults
String.toUpperCase/toLowerCase or any other JVM Unicode table, so the
results are identical on every JVM — the whole point: JDK 8 ships Unicode 6.2 while
JDK 21 ships 15.x, and identifier identity must not depend on which one happens to
run the export.
The operations, deliberately nothing else:
toUpperFull(java.lang.String)/toLowerFull(java.lang.String) — FULL case mappings (length may
change: ß→SS), plus Final_Sigma as the single context rule, implemented
per the UCD definition (NOT any JDK's quirk — the JDKs disagree with each other
on Case_Ignorable contexts, so "match the JVM" is not even well-defined).
Locale-conditional rules (tr/az/lt) are excluded by design: no database folds
identifiers by session locale, and including them corrupts catalog identity.simpleFoldCollapse(java.lang.String) — CaseFolding.txt C+S simple fold, code-point-wise.
Successor of the char-wise caseCollapse (fixes supplementary planes).toUpperSimple(java.lang.String)/toLowerSimple(java.lang.String) — SIMPLE case mappings
(UnicodeData.txt fields 12/13), one code point to one, no context: the mappings
Oracle (unquoted names) and MySQL (table, column and routine names) actually apply.
Derived from the same resource plus a small checked override table, so they add no
resource and change none of the operations above.toLowerAscii(java.lang.String) and clipUtf8Bytes(java.lang.String, int) — PostgreSQL's identifier
downcasing in a UTF8 database and its NAMEDATALEN truncation; no Unicode data.Unpaired surrogates pass through unchanged in every operation — identity must be total on arbitrary UTF-16, and substitution would merge distinct malformed inputs. Inputs the operation does not change are returned as the SAME instance, including surrogate-bearing ones (zero allocation).
Thread-safe: one immutable table holder, safely published via the class
initializer, whose body is straight-line code delegating to ordinary methods.
(Historical note: this class's first obfuscated build failed verification because
proguard.cfg once disabled preverification while emitting v52 class files — that
affected EVERY branchy method product-wide and was fixed by re-enabling
preverification; the branch-free initializer is retained as cheap hygiene.)
Deployment notes: the resource is looked up relative to this class, so package
RELOCATION (consumer-side shading) relocates the lookup with it, but the relocator
must also move the resource directory; GraalVM native-image users must register
gudusoft/gsqlparser/sqlenv/unicode/gsp-case-data.bin as a resource.
Missing/corrupt resource fails fast with ExceptionInInitializerError on
first use.
| Modifier and Type | Field and Description |
|---|---|
static int |
ALGORITHM_REV
Bumped on ANY behavioral change of these operations.
|
static String |
UNICODE_VERSION
Exact UCD version the tables are generated from.
|
| Modifier and Type | Method and Description |
|---|---|
static String |
clipUtf8Bytes(String s,
int maxBytes)
The longest prefix of
s whose UTF-8 encoding has at most maxBytes bytes,
cut on a whole code point (PostgreSQL's pg_mbcliplen in a UTF8 database). |
static String |
simpleFoldCollapse(String s)
CaseFolding C+S simple fold applied per code point — the canonical-equality
collapse for INSENSITIVE + no-fold cells.
|
static String |
toLowerAscii(String s)
ASCII-only lowercase:
A..Z become a..z, every other code
unit is kept. |
static String |
toLowerFull(String s)
Full lowercase mapping with the UCD Final_Sigma rule: Σ lowers to ς iff preceded
by a Cased code point (skipping Case_Ignorable) and NOT followed by one (skipping
Case_Ignorable).
|
static String |
toLowerSimple(String s)
Simple lowercase mapping, code point by code point: each code point becomes its
Simple_Lowercase_Mapping (UnicodeData.txt field 13) or stays itself.
|
static String |
toUpperFull(String s)
Full uppercase mapping (unconditional UCD data; length may grow, e.g.
|
static String |
toUpperSimple(String s)
Simple uppercase mapping, code point by code point: each code point becomes its
Simple_Uppercase_Mapping (UnicodeData.txt field 12) or stays itself.
|
public static final String UNICODE_VERSION
public static final int ALGORITHM_REV
public static String toUpperFull(String s)
public static String toLowerFull(String s)
public static String simpleFoldCollapse(String s)
public static String toUpperSimple(String s)
ß is kept, unlike toUpperFull(java.lang.String)).
This is the per-character mapping Oracle applies to an unquoted name
(straße is stored as STRAßE) and MySQL applies when it compares routine
names (ı and i are one function). It is the SQL-LR UPPER_SIMPLE
operation (lineage-spec/identifier-rules/FORMAT.md section 6).
public static String toLowerSimple(String s)
Σ always becomes σ), and İ becomes i.
This is MySQL's one-to-one lowercase mapping for table names under
lower_case_table_names 1 and 2 and for column names. It is the SQL-LR
LOWER_SIMPLE operation.
public static String toLowerAscii(String s)
A..Z become a..z, every other code
unit is kept. PostgreSQL's downcase_identifier in a multibyte server encoding
(UTF8): ÄBC is stored as Äbc. Consults no Unicode data. Returns the same
instance when nothing changes.public static String clipUtf8Bytes(String s, int maxBytes)
s whose UTF-8 encoding has at most maxBytes bytes,
cut on a whole code point (PostgreSQL's pg_mbcliplen in a UTF8 database). Returns
the same instance when it already fits. An unpaired surrogate counts as three bytes (its
CESU form): it never occurs in a name a server stored, and counting it keeps the
operation total on arbitrary UTF-16.s - the text; null returns nullmaxBytes - the byte limit; zero or negative means no limit