mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
2964 lines
68 KiB
C++
2964 lines
68 KiB
C++
//
|
||
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
|
||
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
||
// McNeel & Associates.
|
||
//
|
||
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
||
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
||
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
||
//
|
||
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
||
//
|
||
////////////////////////////////////////////////////////////////
|
||
|
||
#include "opennurbs.h"
|
||
|
||
#if !defined(ON_COMPILING_OPENNURBS)
|
||
// This check is included in all opennurbs source .c and .cpp files to insure
|
||
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
||
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
||
// and the opennurbs .h files alter what is declared and how it is declared.
|
||
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
||
#endif
|
||
|
||
#define STRING_COMPARE_PREAMBLE(CTYPE,LENGTH,S1,CT1,S2,CT2,EQ,LT,GT) \
|
||
if ( (nullptr == S1 && 0 != CT1) || (nullptr == S2 && 0 != CT2) ) \
|
||
{ if ( S1 == S2 ) return EQ; if (nullptr == S1) return LT; if (nullptr == S2) return GT; } \
|
||
const CTYPE null_terminator = 0; \
|
||
if (CT1 < 0) CT1 = LENGTH(S1); \
|
||
if (CT2 < 0) CT2 = LENGTH(S2); \
|
||
if (0 == CT1) S1 = &null_terminator; \
|
||
if (0 == CT2) S2 = &null_terminator; \
|
||
if (S1 == S2 && CT1 == CT2) return EQ
|
||
|
||
#define WIDE_STRING_COMPARE_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(wchar_t,ON_wString::Length,S1,CT1,S2,CT2,0,-1,1)
|
||
#define CHAR_STRING_COMPARE_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(char,ON_String::Length,S1,CT1,S2,CT2,0,-1,1)
|
||
|
||
#define WIDE_STRING_EQUAL_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(wchar_t,ON_wString::Length,S1,CT1,S2,CT2,true,false,false)
|
||
#define CHAR_STRING_EQUAL_PREAMBLE(S1,CT1,S2,CT2) STRING_COMPARE_PREAMBLE(char,ON_String::Length,S1,CT1,S2,CT2,true,false,false)
|
||
|
||
ON_StringMapOrdinalType ON_StringMapOrdinalTypeFromStringMapType(
|
||
ON_StringMapType map_type
|
||
)
|
||
{
|
||
switch (map_type)
|
||
{
|
||
case ON_StringMapType::UpperCase:
|
||
return ON_StringMapOrdinalType::UpperOrdinal;
|
||
case ON_StringMapType::LowerCase:
|
||
return ON_StringMapOrdinalType::LowerOrdinal;
|
||
case ON_StringMapType::Identity:
|
||
return ON_StringMapOrdinalType::Identity;
|
||
}
|
||
return ON_StringMapOrdinalType::Identity;
|
||
}
|
||
|
||
static ON__UINT32 MapCodePointOrdinal(
|
||
ON__UINT32 unicode_code_point,
|
||
unsigned int maximum_singleton_value,
|
||
ON_StringMapOrdinalType map_type
|
||
)
|
||
{
|
||
// Converts ordinal "char" and "wchar_t" element values in the
|
||
// range 0x00 to maximum_singleton_value to "ignore case" ordinal equivalents.
|
||
// The returned value is always <= input value.
|
||
//
|
||
// This is NOT linguistic and NOT culture invariant.
|
||
//
|
||
// For UTF-8 strings, maximum_singleton_value = 0x7FU
|
||
// For UTF-16 strings, maximum_singleton_value = 0xFFFFU
|
||
// For UTF-32 strings, maximum_singleton_value = 0xFFFFFFFFU
|
||
|
||
// TODO
|
||
// Should ordinal ignore case compares this modify any values > 0x00FFU?
|
||
|
||
if (unicode_code_point < 0x0041U || unicode_code_point > maximum_singleton_value)
|
||
return unicode_code_point;
|
||
|
||
if (unicode_code_point <= 0x005AU)
|
||
{
|
||
// unicode_code_points U+0041 = capital A to U+005A = capital Z
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
{
|
||
return unicode_code_point + 0x20;
|
||
}
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point < 0x0061U)
|
||
{
|
||
// unicode_code_points for the the six glyphs
|
||
// U+005B [
|
||
// U+005C \
|
||
// U+005D ]
|
||
// U+005E ^
|
||
// U+005F _
|
||
// U+0060 `
|
||
// These are the ASCII symbols after capital Z and before lower case A (a)
|
||
// that were inserted so that converting between A...Z and a...z
|
||
// could be done by setting/clearing a single bit 0x20.
|
||
// The first edition of the ASCII standard was published in 1963.
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0x007AU)
|
||
{
|
||
// unicode_code_points u+0061 = lower case A (a) to U+007A = lower case Z (z)
|
||
|
||
if (ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
{
|
||
return unicode_code_point - 0x20;
|
||
}
|
||
|
||
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type)
|
||
{
|
||
return unicode_code_point - 0x20;
|
||
}
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if ( unicode_code_point < 0x00C0U )
|
||
return unicode_code_point;
|
||
|
||
if (unicode_code_point <= 0x00DE)
|
||
{
|
||
// 0x00C0: LATIN CAPITAL LETTER A WITH GRAVE
|
||
// ...
|
||
// 0x00D7: MULTIPLICATION SIGN
|
||
// ...
|
||
// 0x00DE: LATIN CAPITAL LETTER THORN
|
||
if ( unicode_code_point == 0x00D7 )
|
||
return unicode_code_point; // MULTIPLICATION SIGN
|
||
|
||
if ( ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return unicode_code_point + 0x20;
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point < 0x00E0U)
|
||
return unicode_code_point;
|
||
|
||
if (unicode_code_point <= 0x00FEU)
|
||
{
|
||
// 0x00E0: LATIN SMALL LETTER A WITH GRAVE
|
||
// ...
|
||
// 0x00F7: DIVISION SIGN
|
||
// ...
|
||
// 0x00FE: LATIN SMALL LETTER THORN
|
||
if (0x00F7U == unicode_code_point)
|
||
return unicode_code_point; // DIVISION SIGN
|
||
|
||
if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type )
|
||
return unicode_code_point - 0x20;
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0x00FFU)
|
||
{
|
||
// 0x00FFU: LATIN SMALL LETTER Y WITH DIAERESIS
|
||
if ( ON_StringMapOrdinalType::UpperOrdinal == map_type && 0x0178U <= maximum_singleton_value )
|
||
return 0x0178U; // 0x0178U: LATIN CAPITAL LETTER Y WITH DIAERESIS
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0x017FU)
|
||
{
|
||
// Latin Extended-A
|
||
|
||
// special cases
|
||
switch (unicode_code_point)
|
||
{
|
||
case 0x0130U: // LATIN CAPITAL LETTER I WITH DOT ABOVE
|
||
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return 0x0069U; // 0x0069U: LATIN SMALL LETTER i
|
||
return unicode_code_point;
|
||
break;
|
||
|
||
case 0x0131U: // LATIN SMALL LETTER DOTLESS I
|
||
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
return 0x0049U; // 0x0049U LATIN CAPITAL LETTER I
|
||
return unicode_code_point;
|
||
break;
|
||
|
||
case 0x0138U: // LATIN SMALL LETTER KRA
|
||
// No upper case ordinal
|
||
return unicode_code_point;
|
||
break;
|
||
|
||
case 0x0149U: // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
|
||
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
return 0x004EU; // 0x004EU LATIN CAPITAL LETTER N
|
||
return unicode_code_point;
|
||
break;
|
||
|
||
case 0x0178U: // LATIN CAPITAL LETTER Y WITH DIAERESIS
|
||
if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::LowerOrdinal == map_type )
|
||
return 0x00FFU; // 0x00FFU: LATIN SMALL LETTER Y WITH DIAERESIS
|
||
return unicode_code_point;
|
||
break;
|
||
|
||
case 0x017FU: // LATIN SMALL LETTER LONG S
|
||
// No upper case ordinal
|
||
return unicode_code_point;
|
||
break;
|
||
}
|
||
|
||
if (unicode_code_point < 0x0138U || (unicode_code_point >= 0x014AU && unicode_code_point < 0x0178U) )
|
||
{
|
||
// CAPITALS are even and SMALL is CAPITAL+1
|
||
if (0 == (unicode_code_point % 2))
|
||
{
|
||
// LATIN CAPITAL LETTER
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return unicode_code_point + 1;
|
||
}
|
||
else
|
||
{
|
||
// LATIN SMALL LETTER
|
||
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
return unicode_code_point - 1;
|
||
}
|
||
return unicode_code_point;
|
||
}
|
||
else
|
||
{
|
||
// CAPITALS are odd and SMALL is CAPITAL+1
|
||
if (1 == (unicode_code_point % 2))
|
||
{
|
||
// LATIN CAPITAL LETTER
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return unicode_code_point + 1;
|
||
}
|
||
else
|
||
{
|
||
// LATIN SMALL LETTER
|
||
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
return unicode_code_point - 1;
|
||
}
|
||
}
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
|
||
if (unicode_code_point <= 0x01FFU)
|
||
{
|
||
// Latin Extended-B
|
||
|
||
// special cases
|
||
switch (unicode_code_point)
|
||
{
|
||
case 0x0180U: // LATIN SMALL LETTER B WITH STROKE
|
||
if (ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
return 0x0243U;
|
||
return unicode_code_point;
|
||
break;
|
||
case 0x0193U: // LATIN CAPITAL LETTER G WITH HOOK
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return 0x0260U;
|
||
return unicode_code_point;
|
||
break;
|
||
case 0x0194U: // LATIN CAPITAL LETTER GAMMA
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return 0x0263U;
|
||
return unicode_code_point;
|
||
break;
|
||
case 0x0195U: // LATIN SMALL LETTER HV
|
||
if (ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
return 0x01F6U;
|
||
return unicode_code_point;
|
||
break;
|
||
case 0x0196U: // LATIN CAPITAL LETTER IOTA
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return 0x0269U;
|
||
return unicode_code_point;
|
||
break;
|
||
case 0x0197U: // LATIN CAPITAL LETTER I WITH STROKE
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return 0x0268U;
|
||
return unicode_code_point;
|
||
break;
|
||
}
|
||
|
||
// TODO
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
|
||
|
||
if (unicode_code_point < 0x0370)
|
||
return unicode_code_point;
|
||
|
||
if (unicode_code_point <= 0x03FF)
|
||
{
|
||
// Greek and Coptic
|
||
if (unicode_code_point >= 0x0391 && unicode_code_point <= 0x03A9)
|
||
{
|
||
// GREEK CAPITAL LETTER ALPHA ... OMEGA
|
||
if ( 0x03A2 == unicode_code_point )
|
||
return unicode_code_point; // RESERVED
|
||
if ( ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return unicode_code_point + 0x20;
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point >= 0x03B1 && unicode_code_point <= 0x03C9)
|
||
{
|
||
// GREEK SMALL LETTER ALPHA ... OMEGA
|
||
if ( 0x03C2 == unicode_code_point )
|
||
return unicode_code_point; // GREEK SMALL LETTER FINAL SIGMA (stigma)
|
||
if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type )
|
||
return unicode_code_point - 0x20;
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0x377U)
|
||
{
|
||
if ( 0x0374U ==unicode_code_point || 0x0375U ==unicode_code_point )
|
||
return unicode_code_point;
|
||
if (0 == (unicode_code_point % 2))
|
||
{
|
||
// GREEK CAPITAL LETTER
|
||
if ( ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
return unicode_code_point+1;
|
||
}
|
||
else
|
||
{
|
||
// GREEK SMALL LETTER
|
||
if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type )
|
||
return unicode_code_point-1;
|
||
}
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0x037DU)
|
||
{
|
||
if (ON_StringMapOrdinalType::UpperOrdinal == map_type && unicode_code_point >= 0x037BU )
|
||
return unicode_code_point + (0x03FDU - 0x037BU);
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point == 0x037FU || unicode_code_point == 0x03F3U )
|
||
{
|
||
if ( unicode_code_point == 0x037FU && ON_StringMapOrdinalType::LowerOrdinal == map_type )
|
||
return 0x03F3U;
|
||
if ( unicode_code_point == 0x03F3U && ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type ) )
|
||
return 0x03F3U;
|
||
return unicode_code_point;
|
||
}
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if ( unicode_code_point < 0x0400 )
|
||
return unicode_code_point;
|
||
|
||
if ( unicode_code_point <= 0x04FFU )
|
||
{
|
||
// Cyrillic
|
||
if ( unicode_code_point < 0x0410U )
|
||
return unicode_code_point;
|
||
|
||
if (unicode_code_point <= 0x042FU)
|
||
{
|
||
// 0x0410: CYRILLIC CAPITAL LETTER A
|
||
// 0x042F: CYRILLIC CAPITAL LETTER YA
|
||
if ( ON_StringMapOrdinalType::LowerOrdinal == map_type )
|
||
return unicode_code_point + 0x20;
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0x044FU)
|
||
{
|
||
// 0x0430: CYRILLIC SMALL LETTER A -> CYRILLIC CAPITAL LETTER A
|
||
// 0x044F: CYRILLIC SMALL LETTER YA -> CYRILLIC CAPITAL LETTER YA
|
||
if ( ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type )
|
||
return unicode_code_point - 0x20;
|
||
return unicode_code_point;
|
||
}
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point < 0xD800)
|
||
{
|
||
// TODO - deal with other codepoint values < 0xD800
|
||
// That should be reduced for ordinal ignore case compares
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point < 0xFF00U)
|
||
{
|
||
// surrogate pairs use values from 0xD800U to 0xDFFF
|
||
// No ordinal ignore conversion applies to surrogate pair values.
|
||
//
|
||
// 0xE000 to 0xF8FF = unicode private use area
|
||
// No ordinal ignore conversion applies to private use area values.
|
||
}
|
||
|
||
if (unicode_code_point < 0xF900)
|
||
{
|
||
// 0xF900 to 0xFAFF = CJK Compatibility Ideographs
|
||
// 0xFB00... = Alphabetic Presentation Forms
|
||
// ...
|
||
// 0xFE00... = Variation Selectors
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0xFFEFU)
|
||
{
|
||
// Halfwidth and Fullwidth Forms
|
||
if ( unicode_code_point < 0xFF21 )
|
||
return unicode_code_point;
|
||
|
||
if (unicode_code_point <= 0xFF3A)
|
||
{
|
||
// 0xFF22: FULLWIDTH LATIN CAPITAL LETTER A
|
||
// 0xFF3A: FULLWIDTH LATIN CAPITAL LETTER Z
|
||
if (ON_StringMapOrdinalType::LowerOrdinal == map_type)
|
||
{
|
||
return unicode_code_point + 0x20;
|
||
}
|
||
return unicode_code_point;
|
||
}
|
||
|
||
if (unicode_code_point <= 0xFF41)
|
||
return unicode_code_point;
|
||
|
||
if (unicode_code_point <= 0xFF5A)
|
||
{
|
||
if (ON_StringMapOrdinalType::MinimumOrdinal == map_type || ON_StringMapOrdinalType::UpperOrdinal == map_type)
|
||
{
|
||
// 0xFF41: FULLWIDTH LATIN SMALL LETTER A -> FULLWIDTH LATIN CAPITAL LETTER A
|
||
// 0xFF5A: FULLWIDTH LATIN SMALL LETTER Z -> FULLWIDTH LATIN CAPITAL LETTER Z
|
||
return unicode_code_point - 0x20;
|
||
}
|
||
return unicode_code_point;
|
||
}
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
// 0x10000U: Linear B Syllabary
|
||
// 0x10100U: Aegean Numbers
|
||
// ...
|
||
// 0x10FFFFU: maximum valid unicode code point
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
ON__UINT32 ON_UnicodeMapCodePointOrdinal(
|
||
ON_StringMapOrdinalType map_type,
|
||
ON__UINT32 unicode_code_point
|
||
)
|
||
{
|
||
return MapCodePointOrdinal(unicode_code_point,0x10FFFFU,map_type);
|
||
}
|
||
|
||
ON__UINT32 ON_UnicodeMapCodePoint(
|
||
const ON_Locale& locale,
|
||
ON_StringMapType map_type,
|
||
ON__UINT32 unicode_code_point
|
||
)
|
||
{
|
||
if (locale.IsOrdinalOrInvariantCulture())
|
||
return ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalTypeFromStringMapType(map_type), unicode_code_point);
|
||
|
||
wchar_t w[7] = { 0 };
|
||
wchar_t mapped_w[7] = { 0 };
|
||
int w_count = ON_EncodeWideChar(unicode_code_point, 6, w);
|
||
if (w_count > 0)
|
||
{
|
||
int mapped_count = ON_wString::MapString(locale, map_type, w, w_count, mapped_w, sizeof(mapped_w) / sizeof(mapped_w[0]) - 1);
|
||
if (mapped_count > 0)
|
||
{
|
||
ON__UINT32 mapped_unicode_code_point = unicode_code_point;
|
||
ON_UnicodeErrorParameters e;
|
||
if ( mapped_count == ON_DecodeWideChar(w,w_count,&e,&mapped_unicode_code_point) )
|
||
return mapped_unicode_code_point;
|
||
}
|
||
}
|
||
|
||
return unicode_code_point;
|
||
}
|
||
|
||
static unsigned int OrdinalUnsignedToIgnoreCase(
|
||
unsigned int c,
|
||
unsigned int maximum_singleton_value
|
||
)
|
||
{
|
||
// RH-41224
|
||
// map A -> a, ..., Z -> z so underbar is before any "letter".
|
||
// The preserves the behavior of Rhino component name sorting
|
||
// that used "ancient" C runtime ASCII sorts.
|
||
const ON_StringMapOrdinalType map_type
|
||
= ( c <= 0x7A && c >= 0x41 && maximum_singleton_value >= 0x7A )
|
||
? ON_StringMapOrdinalType::LowerOrdinal
|
||
: ON_StringMapOrdinalType::MinimumOrdinal;
|
||
return MapCodePointOrdinal(c, maximum_singleton_value, map_type);
|
||
}
|
||
|
||
static unsigned int OrdinalUTF8ToIgnoreCase(
|
||
char c
|
||
)
|
||
{
|
||
return OrdinalUnsignedToIgnoreCase((unsigned int)c,0x7FU);
|
||
}
|
||
|
||
static unsigned int OrdinalUTF16ToIgnoreCase(
|
||
ON__UINT16 c
|
||
)
|
||
{
|
||
return OrdinalUnsignedToIgnoreCase(c,0xFFFFU);
|
||
}
|
||
|
||
static unsigned int OrdinalUTF32ToIgnoreCase(
|
||
ON__UINT32 c
|
||
)
|
||
{
|
||
return OrdinalUnsignedToIgnoreCase(c,0xFFFFFFFFU);
|
||
}
|
||
|
||
#if ( 2 == ON_SIZEOF_WCHAR_T )
|
||
|
||
static unsigned int OrdinalWideCharToIgnoreCase(
|
||
wchar_t c
|
||
)
|
||
{
|
||
return OrdinalUTF16ToIgnoreCase((ON__UINT16)c);
|
||
}
|
||
#elif ( 4 == ON_SIZEOF_WCHAR_T )
|
||
|
||
|
||
static unsigned int OrdinalWideCharToIgnoreCase(
|
||
wchar_t c
|
||
)
|
||
{
|
||
return OrdinalUTF32ToIgnoreCase((ON__UINT32)c);
|
||
}
|
||
#endif
|
||
|
||
|
||
int ON_StringCompareOrdinalUTF8(
|
||
const char* string1,
|
||
int element_count1,
|
||
const char* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
STRING_COMPARE_PREAMBLE(char,ON_String::Length,string1,element_count1,string2,element_count2,0,-1,1);
|
||
|
||
unsigned int c1, c2;
|
||
int i;
|
||
const int element_count
|
||
= (element_count1 <= element_count2)
|
||
? element_count1
|
||
: element_count2;
|
||
|
||
if (bOrdinalIgnoreCase)
|
||
{
|
||
for (i = 0; i < element_count; i++)
|
||
{
|
||
c1 = OrdinalUTF8ToIgnoreCase(*string1++);
|
||
c2 = OrdinalUTF8ToIgnoreCase(*string2++);
|
||
if ( c1 < c2 )
|
||
return -1;
|
||
if ( c1 > c2 )
|
||
return 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < element_count; i++)
|
||
{
|
||
c1 = (unsigned char)(*string1++);
|
||
c2 = (unsigned char)(*string2++);
|
||
if ( c1 < c2 )
|
||
return -1;
|
||
if ( c1 > c2 )
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count1; i++)
|
||
{
|
||
if ( 0 != (*string1++))
|
||
return 1;
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count2; i++)
|
||
{
|
||
if ( 0 != (*string2++))
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ON_StringCompareOrdinalUTF16(
|
||
const ON__UINT16* string1,
|
||
int element_count1,
|
||
const ON__UINT16* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
STRING_COMPARE_PREAMBLE(ON__UINT16,ON_StringLengthUTF16,string1,element_count1,string2,element_count2,0,-1,1);
|
||
|
||
unsigned int c1, c2;
|
||
int i;
|
||
const int element_count
|
||
= (element_count1 <= element_count2)
|
||
? element_count1
|
||
: element_count2;
|
||
|
||
if (bOrdinalIgnoreCase)
|
||
{
|
||
for (i = 0; i < element_count; i++)
|
||
{
|
||
c1 = OrdinalUTF16ToIgnoreCase(*string1++);
|
||
c2 = OrdinalUTF16ToIgnoreCase(*string2++);
|
||
if ( c1 < c2 )
|
||
return -1;
|
||
if ( c1 > c2 )
|
||
return 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < element_count; i++)
|
||
{
|
||
c1 = (*string1++);
|
||
c2 = (*string2++);
|
||
if ( c1 < c2 )
|
||
return -1;
|
||
if ( c1 > c2 )
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count1; i++)
|
||
{
|
||
if ( 0 != (*string1++))
|
||
return 1;
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count2; i++)
|
||
{
|
||
if ( 0 != (*string2++))
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ON_StringCompareOrdinalUTF32(
|
||
const ON__UINT32* string1,
|
||
int element_count1,
|
||
const ON__UINT32* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
STRING_COMPARE_PREAMBLE(ON__UINT32,ON_StringLengthUTF32,string1,element_count1,string2,element_count2,0,-1,1);
|
||
|
||
unsigned int c1, c2;
|
||
int i;
|
||
const int element_count
|
||
= (element_count1 <= element_count2)
|
||
? element_count1
|
||
: element_count2;
|
||
|
||
if (bOrdinalIgnoreCase)
|
||
{
|
||
for (i = 0; i < element_count; i++)
|
||
{
|
||
c1 = OrdinalUTF32ToIgnoreCase(*string1++);
|
||
c2 = OrdinalUTF32ToIgnoreCase(*string2++);
|
||
if ( c1 < c2 )
|
||
return -1;
|
||
if ( c1 > c2 )
|
||
return 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < element_count; i++)
|
||
{
|
||
c1 = (*string1++);
|
||
c2 = (*string2++);
|
||
if ( c1 < c2 )
|
||
return -1;
|
||
if ( c1 > c2 )
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count1; i++)
|
||
{
|
||
if ( 0 != (*string1++))
|
||
return 1;
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count2; i++)
|
||
{
|
||
if ( 0 != (*string2++))
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ON_StringCompareOrdinalWideChar(
|
||
const wchar_t* string1,
|
||
int element_count1,
|
||
const wchar_t* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
#if (1 == ON_SIZEOF_WCHAR_T)
|
||
return ON_StringCompareOrdinalUTF8((const char*)string1, element_count1, (const char*)string2, element_count2, bOrdinalIgnoreCase);
|
||
#elif (2 == ON_SIZEOF_WCHAR_T)
|
||
return ON_StringCompareOrdinalUTF16((const ON__UINT16*)string1, element_count1, (const ON__UINT16*)string2, element_count2, bOrdinalIgnoreCase);
|
||
#elif (4 == ON_SIZEOF_WCHAR_T)
|
||
return ON_StringCompareOrdinalUTF32((const ON__UINT32*)string1, element_count1, (const ON__UINT32*)string2, element_count2, bOrdinalIgnoreCase);
|
||
#else
|
||
#error ON_SIZEOF_WCHAR_T is not defined or has an unexpected value
|
||
#endif
|
||
}
|
||
|
||
#define ON_UTF8_PATH_SEPARATOR '/'
|
||
static char ON_NormalizeUTF8PathSepartor(
|
||
char c
|
||
)
|
||
{
|
||
return ((c == '/') || (c == '\\')) ? ON_UTF8_PATH_SEPARATOR : c;
|
||
}
|
||
|
||
#define ON_WCHAR_PATH_SEPARATOR ((wchar_t)ON_UTF8_PATH_SEPARATOR)
|
||
static wchar_t ON_NormalizeWideCharPathSepartor(
|
||
wchar_t c
|
||
)
|
||
{
|
||
return ((c == ((wchar_t)'/')) || (c == ((wchar_t)'\\'))) ? ON_WCHAR_PATH_SEPARATOR : c;
|
||
}
|
||
|
||
bool ON_FileSystemPath::PlatformPathIgnoreCase()
|
||
{
|
||
// 2015-June-15 Dale Lear
|
||
// The Windows file system ignores case.
|
||
//
|
||
// Default OS X 10.10.3 installs ignore case - even the "UNIX" shell.
|
||
// User's can customize this setting.
|
||
//
|
||
// For other OS's, devs can customize this code as they see fit.
|
||
return true;
|
||
}
|
||
|
||
int ON_String::ComparePath(
|
||
const char* other_path
|
||
) const
|
||
{
|
||
return ON_String::ComparePath(m_s,-1,other_path,-1);
|
||
}
|
||
|
||
int ON_String::ComparePath(
|
||
const char* path1,
|
||
const char* path2
|
||
)
|
||
{
|
||
return ON_String::ComparePath(path1,-1,path2,-1);
|
||
}
|
||
|
||
int ON_String::ComparePath(
|
||
const char* path1,
|
||
int element_count1,
|
||
const char* path2,
|
||
int element_count2
|
||
)
|
||
{
|
||
CHAR_STRING_COMPARE_PREAMBLE(path1,element_count1,path2,element_count2);
|
||
|
||
const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase();
|
||
|
||
const char* b1 = path1;
|
||
const char* b2 = path2;
|
||
|
||
for(;;)
|
||
{
|
||
int count1 = 0;
|
||
int count2 = 0;
|
||
const char* a1 = b1;
|
||
const char* a2 = b2;
|
||
char c1, c2;
|
||
for (;;)
|
||
{
|
||
if ((int)(b1 - path1) < element_count1)
|
||
{
|
||
c1 = ON_NormalizeUTF8PathSepartor(*b1++);
|
||
if (null_terminator == c1 || ON_UTF8_PATH_SEPARATOR == c1)
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
c1 = null_terminator;
|
||
break;
|
||
}
|
||
count1++;
|
||
}
|
||
|
||
for (;;)
|
||
{
|
||
if ((int)(b2 - path2) < element_count2)
|
||
{
|
||
c2 = ON_NormalizeUTF8PathSepartor(*b2++);
|
||
if (null_terminator == c2 || ON_UTF8_PATH_SEPARATOR == c2)
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
c2 = null_terminator;
|
||
break;
|
||
}
|
||
count2++;
|
||
}
|
||
|
||
if (count1 > 0 || count2 > 0)
|
||
{
|
||
// TODO
|
||
// At this point, we should apply a compare that uses the same UTF-8 string normalization
|
||
// as the file system on the current operating system.
|
||
int rc = ON_StringCompareOrdinalUTF8(a1, count1, a2, count2, bOrdinalIgnoreCase);
|
||
if (0 != rc)
|
||
return rc;
|
||
}
|
||
|
||
if (null_terminator == c1 || null_terminator == c2)
|
||
{
|
||
if ( null_terminator != c2 )
|
||
return -1;
|
||
if ( null_terminator != c1 )
|
||
return 1;
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
bool ON_String::EqualPath(
|
||
const char* other_path
|
||
) const
|
||
{
|
||
return ON_String::EqualPath(m_s,-1,other_path,-1);
|
||
}
|
||
|
||
bool ON_String::EqualPath(
|
||
const char* path1,
|
||
const char* path2
|
||
)
|
||
{
|
||
return ON_String::EqualPath(path1,-1,path2,-1);
|
||
}
|
||
|
||
bool ON_String::EqualPath(
|
||
const char* path1,
|
||
int element_count1,
|
||
const char* path2,
|
||
int element_count2
|
||
)
|
||
{
|
||
CHAR_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2);
|
||
|
||
unsigned int c1=0, c2=0;
|
||
const int element_count
|
||
= (element_count1 <= element_count2)
|
||
? element_count1
|
||
: element_count2;
|
||
int i;
|
||
const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase();
|
||
|
||
if (bOrdinalIgnoreCase)
|
||
{
|
||
for(i=0; i < element_count; i++)
|
||
{
|
||
c1 = ON_NormalizeUTF8PathSepartor((char)OrdinalUTF8ToIgnoreCase(*path1++));
|
||
c2 = ON_NormalizeUTF8PathSepartor((char)OrdinalUTF8ToIgnoreCase(*path2++));
|
||
if ( c1 != c2 )
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for(i=0; i < element_count; i++)
|
||
{
|
||
c1 = ON_NormalizeUTF8PathSepartor(*path1++);
|
||
c2 = ON_NormalizeUTF8PathSepartor(*path2++);
|
||
if ( c1 != c2 )
|
||
return false;
|
||
}
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count1; i++)
|
||
{
|
||
if ( 0 != (*path1++))
|
||
return false;
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count2; i++)
|
||
{
|
||
if ( 0 != (*path2++))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
int ON_wString::ComparePath(
|
||
const wchar_t* other_path
|
||
) const
|
||
{
|
||
return ON_wString::ComparePath(m_s,-1,other_path,-1);
|
||
}
|
||
|
||
int ON_wString::ComparePath(
|
||
const wchar_t* sPath1,
|
||
const wchar_t* sPath2
|
||
)
|
||
{
|
||
return ON_wString::ComparePath(sPath1,-1,sPath2,-1);
|
||
}
|
||
|
||
int ON_wString::ComparePath(
|
||
const wchar_t* path1,
|
||
int element_count1,
|
||
const wchar_t* path2,
|
||
int element_count2
|
||
)
|
||
{
|
||
WIDE_STRING_COMPARE_PREAMBLE(path1,element_count1,path2,element_count2);
|
||
|
||
const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase();
|
||
|
||
const wchar_t* b1 = path1;
|
||
const wchar_t* b2 = path2;
|
||
|
||
for(;;)
|
||
{
|
||
int count1 = 0;
|
||
int count2 = 0;
|
||
const wchar_t* a1 = b1;
|
||
const wchar_t* a2 = b2;
|
||
wchar_t c1, c2;
|
||
for (;;)
|
||
{
|
||
if ((int)(b1 - path1) < element_count1)
|
||
{
|
||
c1 = ON_NormalizeWideCharPathSepartor(*b1++);
|
||
if (null_terminator == c1 || ON_WCHAR_PATH_SEPARATOR == c1)
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
c1 = null_terminator;
|
||
break;
|
||
}
|
||
count1++;
|
||
}
|
||
|
||
for (;;)
|
||
{
|
||
if ((int)(b2 - path2) < element_count2)
|
||
{
|
||
c2 = ON_NormalizeWideCharPathSepartor(*b2++);
|
||
if (null_terminator == c2 || ON_WCHAR_PATH_SEPARATOR == c2)
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
c2 = null_terminator;
|
||
break;
|
||
}
|
||
count2++;
|
||
}
|
||
|
||
if (count1 > 0 || count2 > 0)
|
||
{
|
||
// TODO
|
||
// At this point, we should apply a compare that uses the same UTF-8 string normalization
|
||
// as the file system on the current operating system.
|
||
int rc = ON_StringCompareOrdinalWideChar(a1, count1, a2, count2, bOrdinalIgnoreCase);
|
||
if (0 != rc)
|
||
return rc;
|
||
if (count1 < count2)
|
||
return -1;
|
||
if (count2 < count1)
|
||
return 1;
|
||
}
|
||
|
||
if (null_terminator == c1 || null_terminator == c2)
|
||
{
|
||
if ( null_terminator != c2 )
|
||
return -1;
|
||
if ( null_terminator != c1 )
|
||
return 1;
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
bool ON_wString::EqualPath(
|
||
const wchar_t* other_path
|
||
) const
|
||
{
|
||
return ON_wString::EqualPath(m_s,-1,other_path,-1);
|
||
}
|
||
|
||
bool ON_wString::EqualPath(
|
||
const wchar_t* path1,
|
||
const wchar_t* path2
|
||
)
|
||
{
|
||
return ON_wString::EqualPath(path1,-1,path2,-1);
|
||
}
|
||
|
||
bool ON_wString::EqualPath(
|
||
const wchar_t* path1,
|
||
int element_count1,
|
||
const wchar_t* path2,
|
||
int element_count2
|
||
)
|
||
{
|
||
WIDE_STRING_EQUAL_PREAMBLE(path1,element_count1,path2,element_count2);
|
||
|
||
unsigned int c1=0, c2=0;
|
||
const int element_count
|
||
= (element_count1 <= element_count2)
|
||
? element_count1
|
||
: element_count2;
|
||
int i;
|
||
const bool bOrdinalIgnoreCase = ON_FileSystemPath::PlatformPathIgnoreCase();
|
||
|
||
if (bOrdinalIgnoreCase)
|
||
{
|
||
for(i=0; i < element_count; i++)
|
||
{
|
||
c1 = ON_NormalizeWideCharPathSepartor((wchar_t)OrdinalWideCharToIgnoreCase(*path1++));
|
||
c2 = ON_NormalizeWideCharPathSepartor((wchar_t)OrdinalWideCharToIgnoreCase(*path2++));
|
||
if ( c1 != c2 )
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for(i=0; i < element_count; i++)
|
||
{
|
||
c1 = ON_NormalizeWideCharPathSepartor(*path1++);
|
||
c2 = ON_NormalizeWideCharPathSepartor(*path2++);
|
||
if ( c1 != c2 )
|
||
return false;
|
||
}
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count1; i++)
|
||
{
|
||
if ( 0 != (*path1++))
|
||
return false;
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count2; i++)
|
||
{
|
||
if ( 0 != (*path2++))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
int ON_String::CompareAttributeName(
|
||
const char* other_name
|
||
) const
|
||
{
|
||
const char* name1 = static_cast< const char* >(*this);
|
||
return ON_String::CompareAttributeName(name1, other_name );
|
||
}
|
||
|
||
int ON_String::CompareAttributeName(
|
||
const char* name1,
|
||
const char* name2
|
||
)
|
||
{
|
||
// TODO - normalize and then use a culture aware ignore case test
|
||
return ON_String::CompareOrdinal(name1, -1, name2, -1, true);
|
||
}
|
||
|
||
int ON_wString::CompareAttributeName(
|
||
const wchar_t* other_name
|
||
) const
|
||
{
|
||
const wchar_t* name1 = static_cast< const wchar_t* >(*this);
|
||
return ON_wString::CompareAttributeName(name1, other_name );
|
||
}
|
||
|
||
static unsigned int Internal_NameAttributeWideCharRank(
|
||
wchar_t c,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
// Attribute names have ASCII symbols sorted this way
|
||
|
||
// rank symbol
|
||
// 1 (space)
|
||
// 2 ! (exclamation point)
|
||
// 3 " (double quote)
|
||
// 4 # (hash)
|
||
// 5 $ (dollar)
|
||
// 6 % (percent)
|
||
// 7 & (ampersand)
|
||
// 8 ( (left paren)
|
||
// 9 ) (right paren)
|
||
// 10 * (asterisk)
|
||
// 11 , (comma)
|
||
// 12 . (period)
|
||
// 13 / (slash)
|
||
// 14 : (colon)
|
||
// 15 ; (semicolon)
|
||
// 16 ? (question mark)
|
||
// 17 @ (at)
|
||
// 18 [ (left bracket)
|
||
// 19 \ (back slash)
|
||
// 20 ] (right bracket)
|
||
// 21 ^ (carrot)
|
||
// 22 _ (underscore)
|
||
// 23 ` (back tick)
|
||
// 24 { (left brace)
|
||
// 25 | (pipe)
|
||
// 26 } (right brace)
|
||
// 27 ~ (tilde)
|
||
// 28 + (plus)
|
||
// 29 < (less than)
|
||
// 30 = (equal)
|
||
// 31 > (greater than)
|
||
// 32 0
|
||
// 33 1
|
||
// 34 2
|
||
// 35 3
|
||
// 36 4
|
||
// 37 5
|
||
// 38 6
|
||
// 39 7
|
||
// 40 8
|
||
// 41 9
|
||
// 42 ' (apostrophe)
|
||
// 43 - (hyphen)
|
||
// 44 A
|
||
// 45 B
|
||
// 46 C
|
||
// ...
|
||
// 69 Z
|
||
// 70 a
|
||
// ...
|
||
// 95 z
|
||
|
||
|
||
unsigned int code_point = (unsigned int)c;
|
||
if (0 == code_point)
|
||
return 0;
|
||
|
||
if (code_point < 0x20 || code_point >= 0x7F)
|
||
return ON_UNSET_UINT_INDEX; // no rank - sort some other way
|
||
|
||
// 0 to 9
|
||
if (code_point >= 0x30 && code_point <= 0x39)
|
||
return (code_point - 0x30 + 32);
|
||
|
||
// upper case A to Z
|
||
if (code_point >= 0x41 && code_point <= 0x5A)
|
||
return (code_point - 0x41 + 44);
|
||
|
||
// lower case a to z
|
||
if (code_point >= 0x61 && code_point <= 0x7A)
|
||
return bIgnoreCase ? (code_point - 0x61 + 44) : (code_point - 0x61 + 70);
|
||
|
||
unsigned int rank;
|
||
switch (code_point)
|
||
{
|
||
case 0x20: // 1 (space)
|
||
rank = 1;
|
||
break;
|
||
case 0x21: // 2 ! (exclamation point)
|
||
rank = 2;
|
||
break;
|
||
case 0x22: // 3 " (double quote)
|
||
rank = 3;
|
||
break;
|
||
case 0x23: // 4 # (hash)
|
||
rank = 4;
|
||
break;
|
||
case 0x24: // 5 $ (dollar)
|
||
rank = 5;
|
||
break;
|
||
case 0x25: // 6 % (percent)
|
||
rank = 6;
|
||
break;
|
||
case 0x26: // 7 & (ampersand)
|
||
rank = 7;
|
||
break;
|
||
case 0x28: // 8 ( (left paren)
|
||
rank = 8;
|
||
break;
|
||
case 0x29: // 9 ) (right paren)
|
||
rank = 9;
|
||
break;
|
||
case 0x2A: // 10 * (asterisk)
|
||
rank = 10;
|
||
break;
|
||
case 0x2C: // 11 , (comma)
|
||
rank = 11;
|
||
break;
|
||
case 0x2E: // 12 . (period)
|
||
rank = 12;
|
||
break;
|
||
case 0x2F: // 13 / (slash)
|
||
rank = 13;
|
||
break;
|
||
case 0x3A: // 14 : (colon)
|
||
rank = 14;
|
||
break;
|
||
case 0x3B: // 15 ; (semicolon)
|
||
rank = 15;
|
||
break;
|
||
case 0x3F: // 16 ? (question mark)
|
||
rank = 16;
|
||
break;
|
||
case 0x40: // 17 @ (at)
|
||
rank = 17;
|
||
break;
|
||
case 0x5B: // 18 [ (left bracket)
|
||
rank = 18;
|
||
break;
|
||
case 0x5C: // 19 \ (back slash)
|
||
rank = 19;
|
||
break;
|
||
case 0x5D: // 20 ] (right bracket)
|
||
rank = 20;
|
||
break;
|
||
case 0x5E: // 21 ^ (carrot)
|
||
rank = 21;
|
||
break;
|
||
case 0x5F: // 22 _ (underscore)
|
||
rank = 22;
|
||
break;
|
||
case 0x60: // 23 ` (back tick)
|
||
rank = 23;
|
||
break;
|
||
case 0x7B: // 24 { (left brace)
|
||
rank = 24;
|
||
break;
|
||
case 0x7C: // 25 | (pipe)
|
||
rank = 25;
|
||
break;
|
||
case 0x7D: // 26 } (right brace)
|
||
rank = 26;
|
||
break;
|
||
case 0x7E: // 27 ~ (tilde)
|
||
rank = 27;
|
||
break;
|
||
case 0x2B: // 28 + (plus)
|
||
rank = 28;
|
||
break;
|
||
case 0x3C: // 29 < (less than)
|
||
rank = 29;
|
||
break;
|
||
case 0x3D: // 30 = (equal)
|
||
rank = 30;
|
||
break;
|
||
case 0x3E: // 31 > (greater than)
|
||
rank = 31;
|
||
break;
|
||
|
||
// 32 to 41 numerals 0 to 9 handled before this switch
|
||
|
||
case 0x27: // 42 ' (apostrophe)
|
||
rank = 42;
|
||
break;
|
||
case 0x2D: // 43 - (hyphen)
|
||
rank = 43;
|
||
break;
|
||
|
||
// 43 to 69 upper case A to Z handled before this switch
|
||
|
||
// 43 to 69 lower case z to z handled before this switch
|
||
|
||
default: // no rank - sort some other way
|
||
rank = ON_UNSET_UINT_INDEX;
|
||
break;
|
||
}
|
||
|
||
return rank;
|
||
}
|
||
|
||
int ON_wString::CompareAttributeName(
|
||
const wchar_t* name1,
|
||
const wchar_t* name2
|
||
)
|
||
{
|
||
if (name1 == name2)
|
||
return 0;
|
||
|
||
const bool bIgnoreCase = true;
|
||
|
||
if ( nullptr != name1 && nullptr != name2 )
|
||
{
|
||
// Sort leading underbar before 'A'
|
||
// https://mcneel.myjetbrains.com/youtrack/issue/RH-41224
|
||
|
||
unsigned int rank1 = Internal_NameAttributeWideCharRank(*name1,bIgnoreCase);
|
||
unsigned int rank2 = Internal_NameAttributeWideCharRank(*name2,bIgnoreCase);
|
||
while (rank1 == rank2 && ON_UNSET_UINT_INDEX != rank1 && 0 != rank1)
|
||
{
|
||
name1++;
|
||
name2++;
|
||
rank1 = Internal_NameAttributeWideCharRank(*name1,bIgnoreCase);
|
||
rank2 = Internal_NameAttributeWideCharRank(*name2,bIgnoreCase);
|
||
}
|
||
|
||
if (ON_UNSET_UINT_INDEX != rank1 || ON_UNSET_UINT_INDEX != rank2)
|
||
{
|
||
if (rank1 < rank2)
|
||
return -1;
|
||
if (rank1 > rank2)
|
||
return 1;
|
||
if (0 == rank1)
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// name1 or name2 is nullptr or begins with an unranked code point.
|
||
return ON_wString::CompareOrdinal(name1, -1, name2, -1, bIgnoreCase);
|
||
}
|
||
|
||
bool ON_String::EqualAttributeName(
|
||
const char* other_name
|
||
) const
|
||
{
|
||
const char* name1 = static_cast< const char* >(*this);
|
||
return ON_String::EqualAttributeName(name1, other_name );
|
||
}
|
||
|
||
bool ON_String::EqualAttributeName(
|
||
const char* name1,
|
||
const char* name2
|
||
)
|
||
{
|
||
// TODO - normalize and then use a culture aware ignore case test
|
||
return ON_String::EqualOrdinal(name1, -1, name2, -1, true);
|
||
}
|
||
|
||
bool ON_wString::EqualAttributeName(
|
||
const wchar_t* other_name
|
||
) const
|
||
{
|
||
const wchar_t* name1 = static_cast< const wchar_t* >(*this);
|
||
return ON_wString::EqualAttributeName(name1, other_name );
|
||
}
|
||
|
||
bool ON_wString::EqualAttributeName(
|
||
const wchar_t* name1,
|
||
const wchar_t* name2
|
||
)
|
||
{
|
||
// TODO - normalize and then use a culture aware ignore case test
|
||
return ON_wString::EqualOrdinal(name1, -1, name2, -1, true);
|
||
}
|
||
|
||
int ON_String::Compare( const char* s ) const
|
||
{
|
||
return CompareOrdinal(s,false);
|
||
}
|
||
|
||
int ON_String::Compare( const unsigned char* s) const
|
||
{
|
||
return CompareOrdinal((const char*)s,false);
|
||
}
|
||
|
||
int ON_String::CompareNoCase( const char* s) const
|
||
{
|
||
return CompareOrdinal(s,true);
|
||
}
|
||
|
||
int ON_String::CompareNoCase( const unsigned char* s) const
|
||
{
|
||
return CompareOrdinal((const char*)s,true);
|
||
}
|
||
|
||
bool ON_String::Equal(
|
||
const ON_String& other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_String::Equal(
|
||
static_cast<const char*>(*this),
|
||
this->Length(),
|
||
static_cast<const char*>(other_string),
|
||
other_string.Length(),
|
||
locale,
|
||
bIgnoreCase );
|
||
}
|
||
|
||
bool ON_String::Equal(
|
||
const char* other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_String::Equal(
|
||
static_cast<const char*>(*this),
|
||
this->Length(),
|
||
other_string,
|
||
-1,
|
||
locale,
|
||
bIgnoreCase );
|
||
}
|
||
|
||
bool ON_String::Equal(
|
||
const char* string1,
|
||
const char* string2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
return ON_String::Equal( string1, -1, string2, -1, locale, bIgnoreCase );
|
||
}
|
||
|
||
bool ON_String::Equal(
|
||
const char* string1,
|
||
int element_count1,
|
||
const char* string2,
|
||
int element_count2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
if (locale.IsOrdinal())
|
||
return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase);
|
||
|
||
#if defined(ON_RUNTIME_WIN)
|
||
// TODO - replace with locale aware test
|
||
return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase);
|
||
#elif defined(ON_RUNTIME_APPLE)
|
||
// TODO - replace with locale aware test
|
||
return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase);
|
||
#else
|
||
// TODO - replace with locale aware test
|
||
return ON_String::EqualOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase);
|
||
#endif
|
||
|
||
}
|
||
|
||
|
||
|
||
int ON_String::Compare(
|
||
const ON_String& other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_String::Compare(
|
||
static_cast< const char* >(*this),
|
||
this->Length(),
|
||
static_cast< const char* >(other_string),
|
||
other_string.Length(),
|
||
locale,
|
||
bIgnoreCase
|
||
);
|
||
}
|
||
|
||
|
||
int ON_String::Compare(
|
||
const char* other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_String::Compare(
|
||
static_cast< const char* >(*this),
|
||
this->Length(),
|
||
other_string,
|
||
-1,
|
||
locale,
|
||
bIgnoreCase
|
||
);
|
||
}
|
||
|
||
int ON_String::Compare(
|
||
const char* string1,
|
||
const char* string2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
return ON_String::Compare( string1, -1, string2, -1, locale, bIgnoreCase );
|
||
}
|
||
|
||
int ON_String::Compare(
|
||
const char* string1,
|
||
int element_count1,
|
||
const char* string2,
|
||
int element_count2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
|
||
#if defined(ON_RUNTIME_WIN)
|
||
// TODO
|
||
// Actually do an invariant culture compare
|
||
// This involves NFC normalization and then using the correct CE to compare values.
|
||
//
|
||
return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase );
|
||
//int n = (element_count1 <= element_count2) ? element_count1 : element_count2;
|
||
//int rc
|
||
// = bIgnoreCase
|
||
// ? _strnicmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr())
|
||
// : _strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr());
|
||
//if (0 == rc)
|
||
//{
|
||
// if ( n < element_count1 )
|
||
// rc = ON_String::CompareOrdinal(string1+n,element_count1-n,"",0,bIgnoreCase);
|
||
// else if ( n < element_count2 )
|
||
// rc = ON_String::CompareOrdinal("", 0, string2+n, element_count2-n, bIgnoreCase);
|
||
//}
|
||
//return rc;
|
||
#elif defined(ON_RUNTIME_APPLE)
|
||
// TODO
|
||
// Actually do an invariant culture compare
|
||
// This involves NFC normalization and then using the correct CE to compare values.
|
||
//
|
||
return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase );
|
||
|
||
//int n = (element_count1 <= element_count2) ? element_count1 : element_count2;
|
||
//int rc
|
||
// = bIgnoreCase
|
||
// ? strncasecmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr())
|
||
// : strncmp_l(string1, string2, n, locale.StringCollateAndMapLocalePtr());
|
||
//if (0 == rc)
|
||
//{
|
||
// if ( n < element_count1 )
|
||
// rc = ON_String::CompareOrdinal(string1+n,element_count1-n,"",0,bIgnoreCase);
|
||
// else if ( n < element_count2 )
|
||
// rc = ON_String::CompareOrdinal("", 0, string2+n, element_count2-n, bIgnoreCase);
|
||
//}
|
||
//return rc;
|
||
#else
|
||
return ON_String::CompareOrdinal( string1, -1, string2, -1, bIgnoreCase );
|
||
#endif
|
||
}
|
||
|
||
bool ON_wString::Equal(
|
||
const ON_wString& other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_wString::Equal(
|
||
static_cast< const wchar_t* >(*this),
|
||
this->Length(),
|
||
static_cast< const wchar_t* >(other_string),
|
||
other_string.Length(),
|
||
locale,
|
||
bIgnoreCase
|
||
);
|
||
}
|
||
|
||
bool ON_wString::Equal(
|
||
const wchar_t* other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_wString::Equal(
|
||
static_cast< const wchar_t* >(*this),
|
||
this->Length(),
|
||
other_string,
|
||
-1,
|
||
locale,
|
||
bIgnoreCase
|
||
);
|
||
}
|
||
|
||
bool ON_wString::Equal(
|
||
const wchar_t* string1,
|
||
const wchar_t* string2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
return ON_wString::Equal(string1,-1,string2,-1,locale,bIgnoreCase);
|
||
}
|
||
|
||
bool ON_wString::Equal(
|
||
const wchar_t* string1,
|
||
int element_count1,
|
||
const wchar_t* string2,
|
||
int element_count2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
if ( locale.IsOrdinal() )
|
||
return ON_wString::EqualOrdinal(string1, element_count1, string2, element_count2, bIgnoreCase );
|
||
|
||
#if defined(ON_RUNTIME_WIN)
|
||
return (0 == ON_wString::Compare(string1, element_count1, string2, element_count2, locale, bIgnoreCase ));
|
||
#elif defined(ON_RUNTIME_APPLE)
|
||
return (0 == ON_wString::Compare(string1, element_count1, string2, element_count2, locale, bIgnoreCase ));
|
||
#else
|
||
return ON_wString::EqualOrdinal(string1, element_count1, string2, element_count2, bIgnoreCase );
|
||
#endif
|
||
}
|
||
|
||
int ON_wString::Compare(
|
||
const ON_wString& other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_wString::Compare(
|
||
static_cast< const wchar_t* >(*this),
|
||
this->Length(),
|
||
static_cast< const wchar_t* >(other_string),
|
||
other_string.Length(),
|
||
locale,
|
||
bIgnoreCase
|
||
);
|
||
}
|
||
|
||
int ON_wString::Compare(
|
||
const wchar_t* other_string,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
) const
|
||
{
|
||
return ON_wString::Compare(
|
||
static_cast< const wchar_t* >(*this),
|
||
this->Length(),
|
||
other_string,
|
||
-1,
|
||
locale,
|
||
bIgnoreCase
|
||
);
|
||
}
|
||
|
||
int ON_wString::Compare(
|
||
const wchar_t* string1,
|
||
const wchar_t* string2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
return ON_wString::Compare( string1, -1, string2, -1, locale, bIgnoreCase );
|
||
}
|
||
|
||
int ON_wString::Compare(
|
||
const wchar_t* string1,
|
||
int element_count1,
|
||
const wchar_t* string2,
|
||
int element_count2,
|
||
const class ON_Locale& locale,
|
||
bool bIgnoreCase
|
||
)
|
||
{
|
||
WIDE_STRING_COMPARE_PREAMBLE(string1, element_count1, string2, element_count2);
|
||
|
||
if ( locale.IsOrdinal() )
|
||
return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase);
|
||
|
||
#if defined(ON_RUNTIME_WIN)
|
||
const bool bIsIsInvariantCulture = locale.IsInvariantCulture();
|
||
wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY];
|
||
const wchar_t* locale_name
|
||
= bIsIsInvariantCulture
|
||
? LOCALE_NAME_INVARIANT
|
||
: locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0]));
|
||
|
||
const DWORD flags = ( bIgnoreCase )
|
||
? ((bIsIsInvariantCulture) ? NORM_IGNORECASE : LINGUISTIC_IGNORECASE)
|
||
: 0;
|
||
|
||
int rc = ::CompareStringEx(
|
||
locale_name,
|
||
flags,
|
||
string1,
|
||
element_count1,
|
||
string2,
|
||
element_count2,
|
||
nullptr,
|
||
nullptr,
|
||
0);
|
||
|
||
if (rc == CSTR_LESS_THAN)
|
||
return -1;
|
||
if (rc == CSTR_EQUAL)
|
||
return 0;
|
||
if (rc == CSTR_GREATER_THAN)
|
||
return 1;
|
||
|
||
|
||
#elif defined(ON_RUNTIME_APPLE)
|
||
|
||
// I need a tool that is similar to
|
||
//
|
||
// wcscoll_l(const wchar_t*, const wchar_t*, locale_t )
|
||
//
|
||
// but it needs to take a count (n) and I need one that ignores case.
|
||
//
|
||
// It appears wcsncasecmp_l() does use locale to map case, but then does an ordinal compare.
|
||
//
|
||
//
|
||
//int n = (element_count1 <= element_count2) ? element_count1 : element_count2;
|
||
//int rc
|
||
// = bIgnoreCase
|
||
// ? wcsncasecmp_l(string1, string2, (size_t)n, locale.StringCollateAndMapLocalePtr())
|
||
// : wcsncmp(string1, string2, (size_t)n);
|
||
//if (0 == rc)
|
||
//{
|
||
// if ( n < element_count1 )
|
||
// rc = ON_wString::CompareOrdinal(string1+n,element_count1-n,L"",0,bIgnoreCase);
|
||
// else if ( n < element_count2 )
|
||
// rc = ON_wString::CompareOrdinal(L"", 0, string2+n, element_count2-n, bIgnoreCase);
|
||
//}
|
||
//return rc;
|
||
|
||
return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase);
|
||
|
||
#else
|
||
|
||
#endif
|
||
|
||
return ON_wString::CompareOrdinal(string1,element_count1,string2,element_count2,bIgnoreCase);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// ON_String operator ==, !=, <, >, <=, >=
|
||
//
|
||
|
||
bool operator==( const ON_String& lhs, const ON_String& rhs )
|
||
{
|
||
const int length = lhs.Length();
|
||
return
|
||
(length == rhs.Length())
|
||
&& ON_String::EqualOrdinal(
|
||
static_cast<const char*>(lhs),
|
||
length,
|
||
static_cast<const char*>(rhs),
|
||
length,
|
||
false
|
||
);
|
||
}
|
||
|
||
bool operator==( const ON_String& lhs, const char* rhs )
|
||
{
|
||
const int length = lhs.Length();
|
||
|
||
return (length == ON_String::Length(rhs))
|
||
&& ON_String::EqualOrdinal(
|
||
static_cast<const char*>(lhs),
|
||
length,
|
||
rhs,
|
||
length,
|
||
false
|
||
);
|
||
}
|
||
|
||
bool operator==( const char* lhs, const ON_String& rhs )
|
||
{
|
||
const int length = rhs.Length();
|
||
|
||
return (length == ON_String::Length(lhs))
|
||
&& ON_String::EqualOrdinal(
|
||
lhs,
|
||
length,
|
||
static_cast<const char*>(rhs),
|
||
length,
|
||
false
|
||
);
|
||
}
|
||
|
||
bool operator!=(const ON_String& lhs, const ON_String& rhs)
|
||
{
|
||
return !(lhs == rhs);
|
||
}
|
||
|
||
bool operator!=(const ON_String& lhs, const char* rhs)
|
||
{
|
||
return !(lhs == rhs);
|
||
}
|
||
|
||
bool operator!=(const char* lhs, const ON_String& rhs)
|
||
{
|
||
return !(lhs == rhs);
|
||
}
|
||
|
||
bool operator<( const ON_String& lhs, const ON_String& rhs )
|
||
{
|
||
const int rc = ON_String::CompareOrdinal(
|
||
static_cast<const char*>(lhs),
|
||
lhs.Length(),
|
||
static_cast<const char*>(rhs),
|
||
rhs.Length(),
|
||
false
|
||
);
|
||
return (rc < 0);
|
||
}
|
||
|
||
bool operator<( const ON_String& lhs, const char* rhs )
|
||
{
|
||
const int rc = ON_String::CompareOrdinal(
|
||
static_cast<const char*>(lhs),
|
||
lhs.Length(),
|
||
rhs,
|
||
-1,
|
||
false
|
||
);
|
||
return (rc < 0);
|
||
}
|
||
|
||
bool operator<( const char* lhs, const ON_String& rhs )
|
||
{
|
||
const int rc = ON_String::CompareOrdinal(
|
||
lhs,
|
||
-1,
|
||
static_cast<const char*>(rhs),
|
||
rhs.Length(),
|
||
false
|
||
);
|
||
return (rc < 0);
|
||
}
|
||
|
||
bool operator>( const ON_String& lhs, const ON_String& rhs )
|
||
{
|
||
return operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator>( const ON_String& lhs, const char* rhs )
|
||
{
|
||
return operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator>( const char* lhs, const ON_String& rhs )
|
||
{
|
||
return operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator<=( const ON_String& lhs, const ON_String& rhs )
|
||
{
|
||
return !operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator<=( const ON_String& lhs, const char* rhs )
|
||
{
|
||
return !operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator<=( const char* lhs, const ON_String& rhs )
|
||
{
|
||
return !operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator>=( const ON_String& lhs, const ON_String& rhs )
|
||
{
|
||
return !operator<(lhs,rhs);
|
||
}
|
||
|
||
bool operator>=( const ON_String& lhs, const char* rhs )
|
||
{
|
||
return !operator<(lhs,rhs);
|
||
}
|
||
|
||
bool operator>=( const char* lhs, const ON_String& rhs )
|
||
{
|
||
return !operator<(lhs,rhs);
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// ON_wString operator ==, !=, <, >, <=, >=
|
||
//
|
||
|
||
bool operator==( const ON_wString& lhs, const ON_wString& rhs )
|
||
{
|
||
const int length = lhs.Length();
|
||
return (length == rhs.Length())
|
||
&& ON_wString::EqualOrdinal(
|
||
static_cast<const wchar_t*>(lhs),
|
||
length,
|
||
static_cast<const wchar_t*>(rhs),
|
||
length,
|
||
false
|
||
);
|
||
}
|
||
|
||
bool operator==( const ON_wString& lhs, const wchar_t* rhs )
|
||
{
|
||
const int length = lhs.Length();
|
||
return (length == ON_wString::Length(rhs))
|
||
&& ON_wString::EqualOrdinal(
|
||
static_cast<const wchar_t*>(lhs),
|
||
length,
|
||
rhs,
|
||
length,
|
||
false
|
||
);
|
||
}
|
||
|
||
bool operator==( const wchar_t* lhs, const ON_wString& rhs )
|
||
{
|
||
const int length = rhs.Length();
|
||
return (length == ON_wString::Length(lhs))
|
||
&& ON_wString::EqualOrdinal(
|
||
lhs,
|
||
length,
|
||
static_cast<const wchar_t*>(rhs),
|
||
length,
|
||
false
|
||
);
|
||
}
|
||
|
||
bool operator!=(const ON_wString& lhs, const ON_wString& rhs)
|
||
{
|
||
return !(lhs == rhs);
|
||
}
|
||
|
||
bool operator!=(const ON_wString& lhs, const wchar_t* rhs)
|
||
{
|
||
return !(lhs == rhs);
|
||
}
|
||
|
||
bool operator!=(const wchar_t* lhs, const ON_wString& rhs)
|
||
{
|
||
return !(lhs == rhs);
|
||
}
|
||
|
||
bool operator<( const ON_wString& lhs, const ON_wString& rhs )
|
||
{
|
||
const int rc = ON_wString::CompareOrdinal(
|
||
static_cast<const wchar_t*>(lhs),
|
||
lhs.Length(),
|
||
static_cast<const wchar_t*>(rhs),
|
||
rhs.Length(),
|
||
false
|
||
);
|
||
return (rc < 0);
|
||
}
|
||
|
||
bool operator<( const ON_wString& lhs, const wchar_t* rhs )
|
||
{
|
||
const int rc = ON_wString::CompareOrdinal(
|
||
static_cast<const wchar_t*>(lhs),
|
||
lhs.Length(),
|
||
rhs,
|
||
-1,
|
||
false
|
||
);
|
||
return (rc < 0);
|
||
}
|
||
|
||
bool operator<( const wchar_t* lhs, const ON_wString& rhs )
|
||
{
|
||
const int rc = ON_wString::CompareOrdinal(
|
||
lhs,
|
||
-1,
|
||
static_cast<const wchar_t*>(rhs),
|
||
rhs.Length(),
|
||
false
|
||
);
|
||
return (rc < 0);
|
||
}
|
||
|
||
bool operator>( const ON_wString& lhs, const ON_wString& rhs )
|
||
{
|
||
return operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator>( const ON_wString& lhs, const wchar_t* rhs )
|
||
{
|
||
return operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator>( const wchar_t* lhs, const ON_wString& rhs )
|
||
{
|
||
return operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator<=( const ON_wString& lhs, const ON_wString& rhs )
|
||
{
|
||
return !operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator<=( const ON_wString& lhs, const wchar_t* rhs )
|
||
{
|
||
return !operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator<=( const wchar_t* lhs, const ON_wString& rhs )
|
||
{
|
||
return !operator<(rhs,lhs);
|
||
}
|
||
|
||
bool operator>=( const ON_wString& lhs, const ON_wString& rhs )
|
||
{
|
||
return !operator<(lhs,rhs);
|
||
}
|
||
|
||
bool operator>=( const ON_wString& lhs, const wchar_t* rhs )
|
||
{
|
||
return !operator<(lhs,rhs);
|
||
}
|
||
|
||
bool operator>=( const wchar_t* lhs, const ON_wString& rhs )
|
||
{
|
||
return !operator<(lhs,rhs);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// ON_String::EqualOrdinal
|
||
//
|
||
|
||
bool ON_String::EqualOrdinal(
|
||
const ON_String& other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
const int length = Length();
|
||
return
|
||
(length == other_string.Length())
|
||
&& ON_String::EqualOrdinal(
|
||
static_cast< const char* >(*this),
|
||
length,
|
||
static_cast< const char* >(other_string),
|
||
length,
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
bool ON_String::EqualOrdinal(
|
||
const char* other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
const int length = Length();
|
||
return (length == ON_String::Length(other_string))
|
||
&& ON_String::EqualOrdinal(
|
||
static_cast< const char* >(*this),
|
||
length,
|
||
other_string,
|
||
length,
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
bool ON_String::EqualOrdinal(
|
||
const char* string1,
|
||
const char* string2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
return ON_String::EqualOrdinal(string1,-1,string2,-1,bOrdinalIgnoreCase);
|
||
}
|
||
|
||
bool ON_String::EqualOrdinal(
|
||
const char* string1,
|
||
int element_count1,
|
||
const char* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
CHAR_STRING_EQUAL_PREAMBLE(string1,element_count1,string2,element_count2);
|
||
|
||
const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2;
|
||
int i;
|
||
if (bOrdinalIgnoreCase)
|
||
{
|
||
unsigned int c1, c2;
|
||
for(i = 0; i < element_count; i++)
|
||
{
|
||
c1 = OrdinalUTF8ToIgnoreCase(*string1++);
|
||
c2 = OrdinalUTF8ToIgnoreCase(*string2++);
|
||
if ( c1 != c2 )
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for(i = 0; i < element_count; i++)
|
||
{
|
||
if ((*string1++) != (*string2++))
|
||
return false;
|
||
}
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count1; i++)
|
||
{
|
||
if ( 0 != (*string1++))
|
||
return false;
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count2; i++)
|
||
{
|
||
if ( 0 != (*string2++))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// ON_wString::EqualOrdinal
|
||
//
|
||
|
||
bool ON_wString::EqualOrdinal(
|
||
const ON_wString& other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
const int length = Length();
|
||
return
|
||
(length == other_string.Length())
|
||
&& ON_wString::EqualOrdinal(
|
||
static_cast< const wchar_t* >(*this),
|
||
length,
|
||
static_cast< const wchar_t* >(other_string),
|
||
length,
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
bool ON_wString::EqualOrdinal(
|
||
const wchar_t* other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
const int length = Length();
|
||
return (length == ON_wString::Length(other_string))
|
||
&& ON_wString::EqualOrdinal(
|
||
static_cast< const wchar_t* >(*this),
|
||
length,
|
||
other_string,
|
||
length,
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
bool ON_wString::EqualOrdinal(
|
||
const wchar_t* string1,
|
||
const wchar_t* string2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
return ON_wString::EqualOrdinal(string1,-1,string2,-1,bOrdinalIgnoreCase);
|
||
}
|
||
|
||
bool ON_wString::EqualOrdinal(
|
||
const wchar_t* string1,
|
||
int element_count1,
|
||
const wchar_t* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
WIDE_STRING_EQUAL_PREAMBLE(string1,element_count1,string2,element_count2);
|
||
|
||
const int element_count = (element_count1 <= element_count2) ? element_count1 : element_count2;
|
||
int i;
|
||
if (bOrdinalIgnoreCase)
|
||
{
|
||
unsigned int c1, c2;
|
||
for(i = 0; i < element_count; i++)
|
||
{
|
||
c1 = OrdinalWideCharToIgnoreCase(*string1++);
|
||
c2 = OrdinalWideCharToIgnoreCase(*string2++);
|
||
if ( c1 != c2 )
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for(i = 0; i < element_count; i++)
|
||
{
|
||
if ((*string1++) != (*string2++))
|
||
return false;
|
||
}
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count1; i++)
|
||
{
|
||
if ( 0 != (*string1++))
|
||
return false;
|
||
}
|
||
|
||
for (/*empty init*/; i < element_count2; i++)
|
||
{
|
||
if ( 0 != (*string2++))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// ON_String::CompareOrdinal
|
||
//
|
||
|
||
int ON_String::CompareOrdinal(
|
||
const ON_String& other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
return ON_String::CompareOrdinal(
|
||
static_cast< const char* >(*this),
|
||
this->Length(),
|
||
static_cast< const char* >(other_string),
|
||
other_string.Length(),
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
int ON_String::CompareOrdinal(
|
||
const char* other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
return ON_String::CompareOrdinal(
|
||
static_cast< const char* >(*this),
|
||
this->Length(),
|
||
other_string,
|
||
-1,
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
int ON_String::CompareOrdinal(
|
||
const char* string1,
|
||
const char* string2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
return ON_String::CompareOrdinal(string1, -1, string2, -1, bOrdinalIgnoreCase);
|
||
}
|
||
|
||
int ON_String::CompareOrdinal(
|
||
const char* string1,
|
||
int element_count1,
|
||
const char* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
return ON_StringCompareOrdinalUTF8(string1,element_count1,string2,element_count2,bOrdinalIgnoreCase);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// ON_wString::CompareOrdinal
|
||
//
|
||
|
||
int ON_wString::CompareOrdinal(
|
||
const ON_wString& other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
return ON_wString::CompareOrdinal(
|
||
static_cast< const wchar_t* >(*this),
|
||
this->Length(),
|
||
static_cast< const wchar_t* >(other_string),
|
||
other_string.Length(),
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
int ON_wString::CompareOrdinal(
|
||
const wchar_t* other_string,
|
||
bool bOrdinalIgnoreCase
|
||
) const
|
||
{
|
||
return ON_wString::CompareOrdinal(
|
||
static_cast< const wchar_t* >(*this),
|
||
this->Length(),
|
||
other_string,
|
||
-1,
|
||
bOrdinalIgnoreCase
|
||
);
|
||
}
|
||
|
||
int ON_wString::CompareOrdinal(
|
||
const wchar_t* string1,
|
||
const wchar_t* string2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
return ON_wString::CompareOrdinal(string1, -1, string2, -1, bOrdinalIgnoreCase);
|
||
}
|
||
|
||
int ON_wString::CompareOrdinal(
|
||
const wchar_t* string1,
|
||
int element_count1,
|
||
const wchar_t* string2,
|
||
int element_count2,
|
||
bool bOrdinalIgnoreCase
|
||
)
|
||
{
|
||
return ON_StringCompareOrdinalWideChar(string1,element_count1,string2,element_count2,bOrdinalIgnoreCase);
|
||
}
|
||
|
||
char ON_String::MapCharacterOrdinal(
|
||
ON_StringMapOrdinalType map_type,
|
||
char c
|
||
)
|
||
{
|
||
switch (map_type)
|
||
{
|
||
case ON_StringMapOrdinalType::UpperOrdinal:
|
||
case ON_StringMapOrdinalType::MinimumOrdinal:
|
||
if (c >= 'a' && c <= 'z')
|
||
return c - 0x20;
|
||
break;
|
||
case ON_StringMapOrdinalType::LowerOrdinal:
|
||
if (c >= 'A' && c <= 'Z')
|
||
return c + 0x20;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return c;
|
||
}
|
||
|
||
ON_String ON_String::MapStringOrdinal(
|
||
ON_StringMapOrdinalType map_type
|
||
) const
|
||
{
|
||
ON_String mapped_string(*this);
|
||
if (mapped_string.IsNotEmpty())
|
||
{
|
||
mapped_string.CopyArray();
|
||
const int length = mapped_string.Length();
|
||
ON_String::MapStringOrdinal(map_type, mapped_string.m_s, length, mapped_string.m_s, length);
|
||
}
|
||
return mapped_string;
|
||
}
|
||
|
||
int ON_String::MapStringOrdinal(
|
||
ON_StringMapOrdinalType map_type,
|
||
const char* string,
|
||
int element_count,
|
||
char* mapped_string,
|
||
int mapped_string_capacity
|
||
)
|
||
{
|
||
if (0 != mapped_string_capacity)
|
||
{
|
||
if (nullptr == mapped_string || mapped_string_capacity <= 0)
|
||
return 0;
|
||
|
||
if (string != mapped_string)
|
||
mapped_string[0] = 0;
|
||
}
|
||
|
||
if (element_count < 0)
|
||
{
|
||
element_count = ON_String::Length(string);
|
||
if (element_count < 0)
|
||
return 0;
|
||
if ( 0 == mapped_string_capacity )
|
||
return element_count+1; // +1 for null terminator
|
||
}
|
||
else if ( 0 == mapped_string_capacity )
|
||
return element_count; // no +1 here
|
||
|
||
if ( element_count > mapped_string_capacity )
|
||
return 0;
|
||
|
||
char c;
|
||
const char* s1 = string + element_count;
|
||
switch (map_type)
|
||
{
|
||
case ON_StringMapOrdinalType::UpperOrdinal:
|
||
case ON_StringMapOrdinalType::MinimumOrdinal:
|
||
while (string < s1)
|
||
{
|
||
c = *string++;
|
||
if (c >= 'a' && c <= 'z')
|
||
c -= 0x20;
|
||
*mapped_string++ = c;
|
||
}
|
||
break;
|
||
|
||
case ON_StringMapOrdinalType::LowerOrdinal:
|
||
while (string < s1)
|
||
{
|
||
c = *string++;
|
||
if (c >= 'A' && c <= 'Z')
|
||
c += 0x20;
|
||
*mapped_string++ = c;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
while (string < s1)
|
||
*mapped_string++ = *string++;
|
||
break;
|
||
}
|
||
|
||
if ( element_count < mapped_string_capacity )
|
||
*mapped_string = 0;
|
||
|
||
return element_count;
|
||
}
|
||
|
||
wchar_t ON_wString::MapCharacterOrdinal(
|
||
ON_StringMapOrdinalType map_type,
|
||
wchar_t c
|
||
)
|
||
{
|
||
return (wchar_t)MapCodePointOrdinal( c, 0xFFFFU, map_type );
|
||
}
|
||
|
||
|
||
ON_wString ON_wString::MapStringOrdinal(
|
||
ON_StringMapOrdinalType map_type
|
||
) const
|
||
{
|
||
ON_wString mapped_string(*this);
|
||
if (ON_StringMapOrdinalType::Identity != map_type && mapped_string.IsNotEmpty())
|
||
{
|
||
mapped_string.CopyArray();
|
||
const int length = mapped_string.Length();
|
||
ON_wString::MapStringOrdinal(map_type, mapped_string.m_s, length, mapped_string.m_s, length);
|
||
}
|
||
return mapped_string;
|
||
}
|
||
|
||
int ON_wString::MapStringOrdinal(
|
||
ON_StringMapOrdinalType map_type,
|
||
const wchar_t* string,
|
||
int element_count,
|
||
wchar_t* mapped_string,
|
||
int mapped_string_capacity
|
||
)
|
||
{
|
||
if (0 != mapped_string_capacity)
|
||
{
|
||
if (nullptr == mapped_string || mapped_string_capacity <= 0)
|
||
return 0;
|
||
|
||
if (string != mapped_string)
|
||
mapped_string[0] = 0;
|
||
}
|
||
|
||
if (element_count < 0)
|
||
{
|
||
element_count = ON_wString::Length(string);
|
||
if (element_count < 0)
|
||
return 0;
|
||
if ( 0 == mapped_string_capacity )
|
||
return element_count+1; // +1 for null terminator
|
||
}
|
||
else if ( 0 == mapped_string_capacity )
|
||
return element_count; // no +1 here
|
||
|
||
if ( element_count > mapped_string_capacity )
|
||
return 0;
|
||
|
||
const wchar_t* s1 = string + element_count;
|
||
while (string < s1)
|
||
*mapped_string++ = ON_wString::MapCharacterOrdinal(map_type,*string++);
|
||
|
||
if ( element_count < mapped_string_capacity )
|
||
*mapped_string = 0;
|
||
|
||
return element_count;
|
||
}
|
||
|
||
|
||
ON_String ON_String::MapString(
|
||
const class ON_Locale& locale,
|
||
ON_StringMapType map_type
|
||
) const
|
||
{
|
||
return ON_String::MapString(
|
||
locale,
|
||
map_type,
|
||
static_cast< const char* >(*this),
|
||
Length()
|
||
);
|
||
}
|
||
|
||
ON_wString ON_wString::MapString(
|
||
const class ON_Locale& locale,
|
||
ON_StringMapType map_type
|
||
) const
|
||
{
|
||
return ON_wString::MapString(
|
||
locale,
|
||
map_type,
|
||
static_cast< const wchar_t* >(*this),
|
||
Length()
|
||
);
|
||
}
|
||
|
||
ON_String ON_String::MapString(
|
||
const class ON_Locale& locale,
|
||
ON_StringMapType map_type,
|
||
const char* string,
|
||
int element_count
|
||
)
|
||
{
|
||
bool bMappingNullTerminator;
|
||
|
||
if (element_count < 0)
|
||
{
|
||
element_count = ON_String::Length(string);
|
||
if ( element_count < 0 )
|
||
return ON_String::EmptyString;
|
||
bMappingNullTerminator = false;
|
||
}
|
||
else
|
||
{
|
||
bMappingNullTerminator
|
||
= element_count > 0
|
||
&& nullptr != string
|
||
&& 0 == string[element_count-1]
|
||
&& (1 == element_count || 0 != string[element_count-2])
|
||
;
|
||
}
|
||
|
||
int mapped_string_capacity = ON_String::MapString(locale,map_type,string,element_count,nullptr,0);
|
||
|
||
if (mapped_string_capacity > 0)
|
||
{
|
||
ON_String mapped_string;
|
||
|
||
// reserves mapped_length+1 wchar_t elements in mapped_string.m_s[]
|
||
// and sets mapped_string.m_s[mapped_length] = 0.
|
||
mapped_string.ReserveArray(mapped_string_capacity);
|
||
|
||
// Set mapped_string ON_wString header length value to mapped_length.
|
||
int mapped_string_length
|
||
= ( bMappingNullTerminator )
|
||
? (mapped_string_capacity-1)
|
||
: mapped_string_capacity;
|
||
mapped_string.SetLength(mapped_string_length);
|
||
|
||
int rc = ON_String::MapString(locale,map_type,string,element_count,mapped_string.m_s,mapped_string_capacity);
|
||
if ( rc == mapped_string_capacity )
|
||
return mapped_string;
|
||
}
|
||
|
||
return ON_String::EmptyString;
|
||
}
|
||
|
||
ON_wString ON_wString::MapString(
|
||
const class ON_Locale& locale,
|
||
ON_StringMapType map_type,
|
||
const wchar_t* string,
|
||
int element_count
|
||
)
|
||
{
|
||
bool bMappingNullTerminator;
|
||
|
||
if (element_count < 0)
|
||
{
|
||
element_count = ON_wString::Length(string);
|
||
if ( element_count < 0 )
|
||
return ON_wString::EmptyString;
|
||
bMappingNullTerminator = false;
|
||
}
|
||
else
|
||
{
|
||
bMappingNullTerminator
|
||
= element_count > 0
|
||
&& nullptr != string
|
||
&& 0 == string[element_count-1]
|
||
&& (1 == element_count || 0 != string[element_count-2])
|
||
;
|
||
}
|
||
|
||
int mapped_string_capacity = ON_wString::MapString(locale,map_type,string,element_count,nullptr,0);
|
||
|
||
if (mapped_string_capacity > 0)
|
||
{
|
||
ON_wString mapped_string;
|
||
|
||
// reserves mapped_length+1 wchar_t elements in mapped_string.m_s[]
|
||
// and sets mapped_string.m_s[mapped_length] = 0.
|
||
mapped_string.ReserveArray(mapped_string_capacity);
|
||
|
||
// Set mapped_string ON_wString header length value to mapped_length.
|
||
int mapped_string_length
|
||
= ( bMappingNullTerminator )
|
||
? (mapped_string_capacity-1)
|
||
: mapped_string_capacity;
|
||
mapped_string.SetLength(mapped_string_length);
|
||
|
||
int rc = ON_wString::MapString(locale,map_type,string,element_count,mapped_string.m_s,mapped_string_capacity);
|
||
if ( rc == mapped_string_capacity )
|
||
return mapped_string;
|
||
}
|
||
|
||
return ON_wString::EmptyString;
|
||
}
|
||
|
||
int ON_String::MapString(
|
||
const class ON_Locale& locale,
|
||
ON_StringMapType map_type,
|
||
const char* string,
|
||
int element_count,
|
||
char* mapped_string,
|
||
int mapped_string_capacity
|
||
)
|
||
{
|
||
if (mapped_string_capacity < 0)
|
||
{
|
||
ON_ERROR("mapped_string_capacity is < 0.");
|
||
return 0;
|
||
}
|
||
|
||
if (0 == mapped_string_capacity)
|
||
mapped_string = nullptr;
|
||
else if (mapped_string_capacity > 0)
|
||
{
|
||
if (nullptr == mapped_string)
|
||
{
|
||
ON_ERROR("mapped_string is nullptr.");
|
||
return 0;
|
||
}
|
||
if ( string != mapped_string )
|
||
mapped_string[0] = 0;
|
||
}
|
||
|
||
if (element_count < 0)
|
||
{
|
||
element_count = ON_String::Length(string);
|
||
if (element_count < 0 )
|
||
return 0; // string has more than max int elements - probably missing a null terminator.
|
||
}
|
||
|
||
if ( 0 == element_count)
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
if (nullptr == string)
|
||
{
|
||
ON_ERROR("string is nullptr.");
|
||
return 0;
|
||
}
|
||
|
||
if (false == locale.IsOrdinal())
|
||
{
|
||
const char* s = string;
|
||
const char* s1 = s + element_count;
|
||
while (s < s1)
|
||
{
|
||
char c = *s++;
|
||
if (c >= 0 && c <= 127)
|
||
continue;
|
||
|
||
// UTF-8 multi char element code point
|
||
// No robust UTF-8 tools are available from the Windows or Mac API.
|
||
// TODO - investigate using ICU to avoid UTF-8 -> wide char -> UTF-8 conversion
|
||
const ON_wString wide_string(string, element_count);
|
||
const ON_wString wide_mapped_string = wide_string.MapString(locale, map_type);
|
||
int mapped_element_count = ON_ConvertWideCharToUTF8(
|
||
false, // bTestByteOrder
|
||
static_cast<const wchar_t*>(wide_mapped_string),
|
||
wide_mapped_string.Length(),
|
||
mapped_string,
|
||
mapped_string_capacity,
|
||
nullptr,
|
||
0,
|
||
0,
|
||
nullptr
|
||
);
|
||
|
||
if (mapped_string_capacity > 0 && wide_mapped_string.Length() > mapped_string_capacity)
|
||
{
|
||
ON_ERROR("mapped_string_capacity too small.");
|
||
return 0;
|
||
}
|
||
|
||
if (0 == string[element_count - 1] && mapped_element_count < mapped_string_capacity)
|
||
{
|
||
if (mapped_element_count < mapped_string_capacity)
|
||
{
|
||
mapped_string[mapped_element_count] = 0;
|
||
mapped_element_count++; // count the null terminator
|
||
}
|
||
else
|
||
{
|
||
ON_ERROR("mapped_string_capacity too small.");
|
||
return 0;
|
||
}
|
||
}
|
||
return mapped_element_count;
|
||
}
|
||
}
|
||
|
||
if ( 0 == mapped_string_capacity )
|
||
return element_count;
|
||
|
||
switch (map_type)
|
||
{
|
||
case ON_StringMapType::UpperCase:
|
||
return ON_String::MapStringOrdinal(ON_StringMapOrdinalType::UpperOrdinal,string,element_count,mapped_string,mapped_string_capacity);
|
||
break;
|
||
case ON_StringMapType::LowerCase:
|
||
return ON_String::MapStringOrdinal(ON_StringMapOrdinalType::LowerOrdinal,string,element_count,mapped_string,mapped_string_capacity);
|
||
break;
|
||
default:
|
||
ON_ERROR("invalid map_type.");
|
||
break;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ON_wString::MapString(
|
||
const class ON_Locale& locale,
|
||
ON_StringMapType map_type,
|
||
const wchar_t* string,
|
||
int element_count,
|
||
wchar_t* mapped_string,
|
||
int mapped_string_capacity
|
||
)
|
||
{
|
||
if (mapped_string_capacity < 0)
|
||
{
|
||
ON_ERROR("mapped_string_capacity is < 0.");
|
||
return 0;
|
||
}
|
||
|
||
if (0 == mapped_string_capacity)
|
||
mapped_string = nullptr;
|
||
else if (mapped_string_capacity > 0)
|
||
{
|
||
if (nullptr == mapped_string)
|
||
{
|
||
ON_ERROR("mapped_string is nullptr.");
|
||
return 0;
|
||
}
|
||
if ( string != mapped_string )
|
||
mapped_string[0] = 0;
|
||
}
|
||
|
||
if (element_count < 0)
|
||
{
|
||
element_count = ON_wString::Length(string);
|
||
if (element_count < 0 )
|
||
return 0; // string has more than max int elements - probably missing a null terminator.
|
||
}
|
||
|
||
if ( 0 == element_count)
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
if (nullptr == string)
|
||
{
|
||
ON_ERROR("string is nullptr.");
|
||
return 0;
|
||
}
|
||
|
||
while (false == locale.IsOrdinal())
|
||
{
|
||
|
||
#if defined(ON_RUNTIME_WIN)
|
||
wchar_t buffer[ON_Locale::BUFFER_MAXIMUM_CAPACITY];
|
||
const wchar_t* locale_name
|
||
= locale.IsInvariantCulture()
|
||
? LOCALE_NAME_INVARIANT
|
||
: locale.GetWindowsLocaleName(buffer,sizeof(buffer)/sizeof(buffer[0]));
|
||
|
||
DWORD flags = 0;
|
||
switch (map_type)
|
||
{
|
||
case ON_StringMapType::UpperCase:
|
||
flags = LCMAP_UPPERCASE;
|
||
break;
|
||
case ON_StringMapType::LowerCase:
|
||
flags = LCMAP_LOWERCASE;
|
||
break;
|
||
default:
|
||
ON_ERROR("invalid map_type.");
|
||
return 0;
|
||
}
|
||
|
||
const int mapped_length = LCMapStringEx(
|
||
locale_name,
|
||
flags,
|
||
string,
|
||
element_count,
|
||
mapped_string, mapped_string_capacity,
|
||
nullptr, nullptr, 0
|
||
);
|
||
|
||
if ( mapped_length < mapped_string_capacity && mapped_string_capacity > 0 && nullptr != mapped_string )
|
||
{
|
||
if ( mapped_length <= 0 )
|
||
mapped_string[0] = 0;
|
||
else if ( 0 != mapped_string[mapped_length-1] )
|
||
mapped_string[mapped_length] = 0;
|
||
}
|
||
|
||
return (mapped_length < 0) ? 0 : mapped_length;
|
||
|
||
|
||
#elif defined(ON_RUNTIME_APPLE)
|
||
|
||
if ( 0 == mapped_string_capacity )
|
||
return (element_count > 0 && nullptr != string) ? element_count : 0;
|
||
|
||
if ( mapped_string_capacity < element_count )
|
||
return 0;
|
||
|
||
ON_CRT_locale_t loc = locale.StringCollateAndMapLocalePtr();
|
||
|
||
int mapped_length = 0;
|
||
switch (map_type)
|
||
{
|
||
case ON_StringMapType::UpperCase:
|
||
while (mapped_length < element_count)
|
||
{
|
||
mapped_string[mapped_length] = towupper_l(string[mapped_length], loc);
|
||
mapped_length++;
|
||
}
|
||
break;
|
||
|
||
case ON_StringMapType::LowerCase:
|
||
while (mapped_length < element_count)
|
||
{
|
||
mapped_string[mapped_length] = tolower_l(string[mapped_length], loc);
|
||
mapped_length++;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
ON_ERROR("invalid map_type.");
|
||
return 0;
|
||
}
|
||
|
||
if ( mapped_length < mapped_string_capacity )
|
||
mapped_string[mapped_length] = 0;
|
||
return mapped_length;
|
||
|
||
#else
|
||
break;
|
||
#endif
|
||
}
|
||
|
||
switch (map_type)
|
||
{
|
||
case ON_StringMapType::UpperCase:
|
||
return ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::UpperOrdinal,string,element_count,mapped_string,mapped_string_capacity);
|
||
break;
|
||
case ON_StringMapType::LowerCase:
|
||
return ON_wString::MapStringOrdinal(ON_StringMapOrdinalType::LowerOrdinal,string,element_count,mapped_string,mapped_string_capacity);
|
||
break;
|
||
default:
|
||
ON_ERROR("invalid map_type.");
|
||
break;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void ON_wString::MakeUpper()
|
||
{
|
||
MakeUpperOrdinal();
|
||
}
|
||
|
||
void ON_wString::MakeLower()
|
||
{
|
||
MakeLowerOrdinal();
|
||
}
|
||
|
||
void ON_String::MakeUpper()
|
||
{
|
||
MakeUpperOrdinal();
|
||
}
|
||
|
||
void ON_String::MakeLower()
|
||
{
|
||
MakeLowerOrdinal();
|
||
}
|
||
|
||
|
||
void ON_wString::MakeUpperOrdinal()
|
||
{
|
||
CopyArray();
|
||
const int length = Length();
|
||
if (length > 0)
|
||
ON_wString::MapStringOrdinal(
|
||
ON_StringMapOrdinalType::UpperOrdinal,
|
||
m_s,
|
||
length,
|
||
m_s,
|
||
length
|
||
);
|
||
}
|
||
|
||
void ON_wString::MakeLowerOrdinal()
|
||
{
|
||
CopyArray();
|
||
const int length = Length();
|
||
if (length > 0)
|
||
ON_wString::MapStringOrdinal(
|
||
ON_StringMapOrdinalType::LowerOrdinal,
|
||
m_s,
|
||
length,
|
||
m_s,
|
||
length
|
||
);
|
||
}
|
||
|
||
void ON_String::MakeUpperOrdinal()
|
||
{
|
||
CopyArray();
|
||
const int length = Length();
|
||
if (length > 0)
|
||
ON_String::MapStringOrdinal(
|
||
ON_StringMapOrdinalType::UpperOrdinal,
|
||
m_s,
|
||
length,
|
||
m_s,
|
||
length
|
||
);
|
||
}
|
||
|
||
void ON_String::MakeLowerOrdinal()
|
||
{
|
||
CopyArray();
|
||
const int length = Length();
|
||
if (length > 0)
|
||
ON_String::MapStringOrdinal(
|
||
ON_StringMapOrdinalType::LowerOrdinal,
|
||
m_s,
|
||
length,
|
||
m_s,
|
||
length
|
||
);
|
||
}
|