github.com/matrixorigin/matrixone@v1.2.0/pkg/vectorize/format/format.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package format 16 17 import ( 18 "bytes" 19 "strconv" 20 "strings" 21 "unicode" 22 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 ) 25 26 // FormatFunc is the locale format function signature. 27 type FormatFunc func(string, string) (string, error) 28 29 func GetNumberFormat(number, scale, locale string) (string, error) { 30 return getFormatFunctionWithLocale(locale)(number, scale) 31 } 32 33 // GetFormatFunctionWithLocate get the format function for sepcific locale. 34 func getFormatFunctionWithLocale(locale string) FormatFunc { 35 formatFunc, exist := localeToFormatFunction[locale] 36 if !exist { 37 return formatENUS 38 } 39 return formatFunc 40 } 41 42 // localeToFormatFunction is the string represent of locale format function. 43 var localeToFormatFunction = map[string]FormatFunc{ 44 //formatENUS 45 "ar_AE": formatENUS, 46 "ar_BH": formatENUS, 47 "ar_DZ": formatENUS, 48 "ar_EG": formatENUS, 49 "ar_IN": formatENUS, 50 "ar_IQ": formatENUS, 51 "ar_JO": formatENUS, 52 "ar_KW": formatENUS, 53 "ar_LB": formatENUS, 54 "ar_LY": formatENUS, 55 "ar_MA": formatENUS, 56 "ar_OM": formatENUS, 57 "ar_QA": formatENUS, 58 "ar_SD": formatENUS, 59 "ar_SY": formatENUS, 60 "ar_TN": formatENUS, 61 "ar_YE": formatENUS, 62 "en_AU": formatENUS, 63 "en_CA": formatENUS, 64 "en_GB": formatENUS, 65 "en_IN": formatENUS, 66 "en_NZ": formatENUS, 67 "en_PH": formatENUS, 68 "en_US": formatENUS, 69 "en_ZA": formatENUS, 70 "en_ZW": formatENUS, 71 "es_DO": formatENUS, 72 "es_GT": formatENUS, 73 "es_HN": formatENUS, 74 "en_MX": formatENUS, 75 "es_NI": formatENUS, 76 "es_PA": formatENUS, 77 "es_PR": formatENUS, 78 "es_SV": formatENUS, 79 "es_US": formatENUS, 80 "gu_IN": formatENUS, 81 "he_IL": formatENUS, 82 "hi_IN": formatENUS, 83 "ja_JP": formatENUS, 84 "ko_KR": formatENUS, 85 "ms_MY": formatENUS, 86 "ta_IN": formatENUS, 87 "te_IN": formatENUS, 88 "th_TH": formatENUS, 89 "ur_PK": formatENUS, 90 "zh_CN": formatENUS, 91 "zh_HK": formatENUS, 92 "zh_TW": formatENUS, 93 94 //formatARSA 95 "ar_SA": formatARSA, 96 "sr_RS": formatARSA, 97 98 //formatBEBY 99 "be_BY": formatBEBY, 100 "da_DK": formatBEBY, 101 "de_BE": formatBEBY, 102 "de_DE": formatBEBY, 103 "de_LU": formatBEBY, 104 "es_AR": formatBEBY, 105 "es_BO": formatBEBY, 106 "es_CL": formatBEBY, 107 "es_CO": formatBEBY, 108 "es_EC": formatBEBY, 109 "es_ES": formatBEBY, 110 "es_PY": formatBEBY, 111 "es_UY": formatBEBY, 112 "es_VE": formatBEBY, 113 "fo_FO": formatBEBY, 114 "hu_HU": formatBEBY, 115 "id_ID": formatBEBY, 116 "is_IS": formatBEBY, 117 "lt_LT": formatBEBY, 118 "mn_MN": formatBEBY, 119 "nb_NO": formatBEBY, 120 "no_NO": formatBEBY, 121 "ru_UA": formatBEBY, 122 "sq_AL": formatBEBY, 123 "tr_TR": formatBEBY, 124 "uk_UA": formatBEBY, 125 "vi_VN": formatBEBY, 126 "ro_RO": formatBEBY, 127 128 //formatBGBG 129 "bg_BG": formatBGBG, 130 "cs_CZ": formatBGBG, 131 "es_CR": formatBGBG, 132 "et_EE": formatBGBG, 133 "fi_FI": formatBGBG, 134 "lv_LV": formatBGBG, 135 "mk_MK": formatBGBG, 136 "ru_RU": formatBGBG, 137 "sk_SK": formatBGBG, 138 "sv_FI": formatBGBG, 139 "sv_SE": formatBGBG, 140 141 //formatDECH 142 "de_CH": formatDECH, 143 144 //formatCAES 145 "ca_ES": formatCAES, 146 "de_AT": formatCAES, 147 "el_GR": formatCAES, 148 "eu_ES": formatCAES, 149 "fr_BE": formatCAES, 150 "fr_CA": formatCAES, 151 "fr_CH": formatCAES, 152 "fr_FR": formatCAES, 153 "fr_LU": formatCAES, 154 "gl_ES": formatCAES, 155 "hr_HR": formatCAES, 156 "it_IT": formatCAES, 157 "nl_BE": formatCAES, 158 "nl_NL": formatCAES, 159 "pl_PL": formatCAES, 160 "pt_BR": formatCAES, 161 "pt_PT": formatCAES, 162 "sl_SI": formatCAES, 163 164 //formatITCH 165 "it_CH": formatITCH, 166 "rm_CH": formatITCH, 167 } 168 169 // format number like 20,000,000.0000 170 func formatENUS(number string, scale string) (string, error) { 171 return format(number, scale, []byte{','}, []byte{'.'}) 172 } 173 174 // format number like 20000000.0000 175 func formatARSA(number string, scale string) (string, error) { 176 return format(number, scale, []byte{}, []byte{'.'}) 177 } 178 179 // format number like 20.000.000,0000 180 func formatBEBY(number string, scale string) (string, error) { 181 return format(number, scale, []byte{'.'}, []byte{','}) 182 } 183 184 // format number like 20 000 000,0000 185 func formatBGBG(number string, scale string) (string, error) { 186 return format(number, scale, []byte{' '}, []byte{','}) 187 } 188 189 // format number like 20'000'000.0000 190 func formatDECH(number string, scale string) (string, error) { 191 return format(number, scale, []byte{'\''}, []byte{'.'}) 192 } 193 194 // format number like 20000000,0000 195 func formatCAES(number string, scale string) (string, error) { 196 return format(number, scale, []byte{}, []byte{','}) 197 } 198 199 // format number like 20'000'000.0000 200 func formatITCH(number string, scale string) (string, error) { 201 return format(number, scale, []byte{'\''}, []byte{','}) 202 } 203 204 func format(number string, scale string, comma, decimalPoint []byte) (string, error) { 205 var buffer bytes.Buffer 206 207 if len(number) == 0 { 208 return "", nil 209 } 210 //handle scale 211 if unicode.IsDigit(rune(scale[0])) { 212 for i, v := range scale { 213 if unicode.IsDigit(v) { 214 continue 215 } 216 scale = scale[:i] 217 break 218 } 219 } else { 220 scale = "0" 221 } 222 223 //handle number 224 if number[0] == '-' && number[1] == '.' { 225 number = strings.Replace(number, "-", "-0", 1) 226 } else if number[0] == '.' { 227 number = strings.Replace(number, ".", "0.", 1) 228 } 229 230 if (number[:1] == "-" && !unicode.IsDigit(rune(number[1]))) || 231 (!unicode.IsDigit(rune(number[0])) && number[:1] != "-") { 232 buffer.Write([]byte{'0'}) 233 position, err := strconv.ParseUint(scale, 10, 64) 234 if err == nil && position > 0 { 235 buffer.Write([]byte{'.'}) 236 buffer.WriteString(strings.Repeat("0", int(position))) 237 } 238 return buffer.String(), nil 239 } else if number[:1] == "-" { 240 buffer.Write([]byte{'-'}) 241 number = number[1:] 242 } 243 244 // Check for scientific notition. 245 for _, v := range number { 246 if v == 'E' || v == 'e' { 247 num, err := strconv.ParseFloat(number, 64) 248 if err != nil { 249 return "", err 250 } 251 // Convert to non-scientific notition. 252 number = strconv.FormatFloat(num, 'f', -1, 64) 253 break 254 } 255 } 256 257 for i, v := range number { 258 if unicode.IsDigit(v) { 259 continue 260 } else if i == 1 && number[1] == '.' { 261 continue 262 } else if v == '.' && number[1] != '.' { 263 continue 264 } else { 265 number = number[:i] 266 break 267 } 268 } 269 270 parts := strings.Split(number, ".") 271 272 if len(comma) != 0 { 273 addComma(comma, &buffer, parts[0]) 274 } else { 275 buffer.WriteString(parts[0]) 276 } 277 278 //According to the scale to process the decimal parts 279 position, err := strconv.ParseUint(scale, 10, 64) 280 if err != nil { 281 return "", err 282 } 283 284 if position > 0 { 285 buffer.Write(decimalPoint) 286 if len(parts) == 2 { 287 if uint64(len(parts[1])) == position { 288 buffer.WriteString(parts[1][:position]) 289 } else if uint64(len(parts[1])) > position { 290 //need to cast decimal's length 291 if parts[1][position:position+1] >= "5" { 292 buffer.Reset() 293 floatVar, err := strconv.ParseFloat(parts[0]+"."+parts[1], 64) 294 if err != nil { 295 return "", moerr.NewInvalidArgNoCtx("format parser error", parts[0]+"."+parts[1]) 296 } 297 newNumber := strconv.FormatFloat(floatVar, 'f', int(position), 64) 298 newParts := strings.Split(newNumber, ".") 299 300 if len(comma) != 0 { 301 addComma(comma, &buffer, newParts[0]) 302 } else { 303 buffer.WriteString(newParts[0]) 304 } 305 306 buffer.Write(decimalPoint) 307 buffer.WriteString(newParts[1]) 308 } else { 309 buffer.WriteString(parts[1][:position]) 310 } 311 } else { 312 buffer.WriteString(parts[1]) 313 buffer.WriteString(strings.Repeat("0", int(position)-len(parts[1]))) 314 } 315 } else { 316 buffer.WriteString(strings.Repeat("0", int(position))) 317 } 318 } 319 320 return buffer.String(), nil 321 } 322 323 func addComma(comma []byte, buffer *bytes.Buffer, formatString string) { 324 pos := 0 325 326 //Add foramt comma for Integr parts 327 //If the integer part's length larger than 3 328 if len(formatString)%3 != 0 { 329 pos += len(formatString) % 3 330 buffer.WriteString(formatString[:pos]) 331 buffer.Write(comma) 332 } 333 //Add a format comma every three digits 334 for ; pos < len(formatString); pos += 3 { 335 buffer.WriteString(formatString[pos : pos+3]) 336 buffer.Write(comma) 337 } 338 339 buffer.Truncate(buffer.Len() - 1) 340 341 }