github.com/matrixorigin/matrixone@v0.7.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 Format(numbers, precisions, locales []string, rowCount int, constVectors []bool, results []string) error {
    30  	var err error
    31  	if constVectors[0] {
    32  		number := numbers[0]
    33  		if constVectors[1] {
    34  			precision := precisions[0]
    35  			if constVectors[2] {
    36  				//scalar - scalar - scalar
    37  				results[0], err = getNumberFormat(number, precision, locales[0])
    38  				if err != nil {
    39  					return err
    40  				}
    41  			} else {
    42  				//scalar - scalar - vector
    43  				for i := 0; i < rowCount; i++ {
    44  					results[i], err = getNumberFormat(number, precision, locales[i])
    45  					if err != nil {
    46  						return err
    47  					}
    48  				}
    49  			}
    50  		} else {
    51  			if constVectors[2] {
    52  				locale := locales[0]
    53  				//scalar - vector - scalar
    54  				for i := 0; i < rowCount; i++ {
    55  					results[i], err = getNumberFormat(number, precisions[i], locale)
    56  					if err != nil {
    57  						return err
    58  					}
    59  				}
    60  			} else {
    61  				//scalar - vector - vector
    62  				for i := 0; i < rowCount; i++ {
    63  					results[i], err = getNumberFormat(number, precisions[i], locales[i])
    64  					if err != nil {
    65  						return err
    66  					}
    67  				}
    68  			}
    69  		}
    70  	} else {
    71  		if constVectors[1] {
    72  			precision := precisions[0]
    73  			if constVectors[2] {
    74  				locale := locales[0]
    75  				//vector - scalar - scalar
    76  				for i := 0; i < rowCount; i++ {
    77  					results[i], err = getNumberFormat(numbers[i], precision, locale)
    78  					if err != nil {
    79  						return err
    80  					}
    81  				}
    82  			} else {
    83  				//vaetor - scalar - vector
    84  				for i := 0; i < rowCount; i++ {
    85  					results[i], err = getNumberFormat(numbers[i], precision, locales[i])
    86  					if err != nil {
    87  						return err
    88  					}
    89  				}
    90  			}
    91  		} else {
    92  			if constVectors[2] {
    93  				locale := locales[0]
    94  				//vector - vector - scalar
    95  				for i := 0; i < rowCount; i++ {
    96  					results[i], err = getNumberFormat(numbers[i], precisions[i], locale)
    97  					if err != nil {
    98  						return err
    99  					}
   100  				}
   101  			} else {
   102  				//vector - vector - vector
   103  				for i := 0; i < rowCount; i++ {
   104  					results[i], err = getNumberFormat(numbers[i], precisions[i], locales[i])
   105  					if err != nil {
   106  						return err
   107  					}
   108  				}
   109  			}
   110  		}
   111  	}
   112  	return nil
   113  }
   114  
   115  func getNumberFormat(number, precision, locale string) (string, error) {
   116  	return getFormatFunctionWithLocale(locale)(number, precision)
   117  }
   118  
   119  // GetFormatFunctionWithLocate get the format function for sepcific locale.
   120  func getFormatFunctionWithLocale(locale string) FormatFunc {
   121  	formatFunc, exist := localeToFormatFunction[locale]
   122  	if !exist {
   123  		return formatENUS
   124  	}
   125  	return formatFunc
   126  }
   127  
   128  // localeToFormatFunction is the string represent of locale format function.
   129  var localeToFormatFunction = map[string]FormatFunc{
   130  	//formatENUS
   131  	"ar_AE": formatENUS,
   132  	"ar_BH": formatENUS,
   133  	"ar_DZ": formatENUS,
   134  	"ar_EG": formatENUS,
   135  	"ar_IN": formatENUS,
   136  	"ar_IQ": formatENUS,
   137  	"ar_JO": formatENUS,
   138  	"ar_KW": formatENUS,
   139  	"ar_LB": formatENUS,
   140  	"ar_LY": formatENUS,
   141  	"ar_MA": formatENUS,
   142  	"ar_OM": formatENUS,
   143  	"ar_QA": formatENUS,
   144  	"ar_SD": formatENUS,
   145  	"ar_SY": formatENUS,
   146  	"ar_TN": formatENUS,
   147  	"ar_YE": formatENUS,
   148  	"en_AU": formatENUS,
   149  	"en_CA": formatENUS,
   150  	"en_GB": formatENUS,
   151  	"en_IN": formatENUS,
   152  	"en_NZ": formatENUS,
   153  	"en_PH": formatENUS,
   154  	"en_US": formatENUS,
   155  	"en_ZA": formatENUS,
   156  	"en_ZW": formatENUS,
   157  	"es_DO": formatENUS,
   158  	"es_GT": formatENUS,
   159  	"es_HN": formatENUS,
   160  	"en_MX": formatENUS,
   161  	"es_NI": formatENUS,
   162  	"es_PA": formatENUS,
   163  	"es_PR": formatENUS,
   164  	"es_SV": formatENUS,
   165  	"es_US": formatENUS,
   166  	"gu_IN": formatENUS,
   167  	"he_IL": formatENUS,
   168  	"hi_IN": formatENUS,
   169  	"ja_JP": formatENUS,
   170  	"ko_KR": formatENUS,
   171  	"ms_MY": formatENUS,
   172  	"ta_IN": formatENUS,
   173  	"te_IN": formatENUS,
   174  	"th_TH": formatENUS,
   175  	"ur_PK": formatENUS,
   176  	"zh_CN": formatENUS,
   177  	"zh_HK": formatENUS,
   178  	"zh_TW": formatENUS,
   179  
   180  	//formatARSA
   181  	"ar_SA": formatARSA,
   182  	"ca_ES": formatARSA,
   183  	"de_AT": formatARSA,
   184  	"el_GR": formatARSA,
   185  	"eu_ES": formatARSA,
   186  	"fr_FR": formatARSA,
   187  	"fr_LU": formatARSA,
   188  	"gl_ES": formatARSA,
   189  	"hr_HR": formatARSA,
   190  	"it_IT": formatARSA,
   191  	"nl_BE": formatARSA,
   192  	"nl_NL": formatARSA,
   193  	"pl_PL": formatARSA,
   194  	"pt_BR": formatARSA,
   195  	"pt_PT": formatARSA,
   196  	"sl_SI": formatARSA,
   197  	"sr_RS": formatARSA,
   198  
   199  	//formatBEBY
   200  	"be_BY": formatBEBY,
   201  	"da_DK": formatBEBY,
   202  	"de_BE": formatBEBY,
   203  	"de_DE": formatBEBY,
   204  	"de_LU": formatBEBY,
   205  	"es_AR": formatBEBY,
   206  	"es_BO": formatBEBY,
   207  	"es_CL": formatBEBY,
   208  	"es_CO": formatBEBY,
   209  	"es_EC": formatBEBY,
   210  	"es_ES": formatBEBY,
   211  	"es_PY": formatBEBY,
   212  	"es_UY": formatBEBY,
   213  	"es_VE": formatBEBY,
   214  	"fo_FO": formatBEBY,
   215  	"hu_HU": formatBEBY,
   216  	"id_ID": formatBEBY,
   217  	"is_IS": formatBEBY,
   218  	"lt_LT": formatBEBY,
   219  	"mn_MN": formatBEBY,
   220  	"nb_NO": formatBEBY,
   221  	"no_NO": formatBEBY,
   222  	"ru_UA": formatBEBY,
   223  	"sq_AL": formatBEBY,
   224  	"tr_TR": formatBEBY,
   225  	"uk_UA": formatBEBY,
   226  	"vi_VN": formatBEBY,
   227  
   228  	//formatBGBG
   229  	"bg_BG": formatBGBG,
   230  	"cs_CZ": formatBGBG,
   231  	"es_CR": formatBGBG,
   232  	"et_EE": formatBGBG,
   233  	"fi_FI": formatBGBG,
   234  	"lv_LV": formatBGBG,
   235  	"mk_MK": formatBGBG,
   236  	"ru_RU": formatBGBG,
   237  	"sk_SK": formatBGBG,
   238  	"sv_FI": formatBGBG,
   239  	"sv_SE": formatBGBG,
   240  
   241  	//formatDECH
   242  	"de_CH": formatDECH,
   243  	"it_CH": formatDECH,
   244  	"rm_CH": formatDECH,
   245  }
   246  
   247  // format number like 20,000,000.0000
   248  func formatENUS(number string, precision string) (string, error) {
   249  	return format(number, precision, []byte{','}, []byte{'.'})
   250  }
   251  
   252  // format number like 20000000.0000
   253  func formatARSA(number string, precision string) (string, error) {
   254  	return format(number, precision, []byte{}, []byte{'.'})
   255  }
   256  
   257  // format number like 20.000.000,0000
   258  func formatBEBY(number string, precision string) (string, error) {
   259  	return format(number, precision, []byte{'.'}, []byte{','})
   260  }
   261  
   262  // format number like 20 000 000,0000
   263  func formatBGBG(number string, precision string) (string, error) {
   264  	return format(number, precision, []byte{' '}, []byte{','})
   265  }
   266  
   267  // format number like 20'000'000.0000
   268  func formatDECH(number string, precision string) (string, error) {
   269  	return format(number, precision, []byte{'\''}, []byte{'.'})
   270  }
   271  
   272  func format(number string, precision string, comma, decimalPoint []byte) (string, error) {
   273  	var buffer bytes.Buffer
   274  
   275  	if len(number) == 0 {
   276  		return "", nil
   277  	}
   278  	//handle precision
   279  	if unicode.IsDigit(rune(precision[0])) {
   280  		for i, v := range precision {
   281  			if unicode.IsDigit(v) {
   282  				continue
   283  			}
   284  			precision = precision[:i]
   285  			break
   286  		}
   287  	} else {
   288  		precision = "0"
   289  	}
   290  
   291  	//handle number
   292  	if number[0] == '-' && number[1] == '.' {
   293  		number = strings.Replace(number, "-", "-0", 1)
   294  	} else if number[0] == '.' {
   295  		number = strings.Replace(number, ".", "0.", 1)
   296  	}
   297  
   298  	if (number[:1] == "-" && !unicode.IsDigit(rune(number[1]))) ||
   299  		(!unicode.IsDigit(rune(number[0])) && number[:1] != "-") {
   300  		buffer.Write([]byte{'0'})
   301  		position, err := strconv.ParseUint(precision, 10, 64)
   302  		if err == nil && position > 0 {
   303  			buffer.Write([]byte{'.'})
   304  			buffer.WriteString(strings.Repeat("0", int(position)))
   305  		}
   306  		return buffer.String(), nil
   307  	} else if number[:1] == "-" {
   308  		buffer.Write([]byte{'-'})
   309  		number = number[1:]
   310  	}
   311  
   312  	// Check for scientific notition.
   313  	for _, v := range number {
   314  		if v == 'E' || v == 'e' {
   315  			num, err := strconv.ParseFloat(number, 64)
   316  			if err != nil {
   317  				return "", err
   318  			}
   319  			// Convert to non-scientific notition.
   320  			number = strconv.FormatFloat(num, 'f', -1, 64)
   321  			break
   322  		}
   323  	}
   324  
   325  	for i, v := range number {
   326  		if unicode.IsDigit(v) {
   327  			continue
   328  		} else if i == 1 && number[1] == '.' {
   329  			continue
   330  		} else if v == '.' && number[1] != '.' {
   331  			continue
   332  		} else {
   333  			number = number[:i]
   334  			break
   335  		}
   336  	}
   337  
   338  	parts := strings.Split(number, ".")
   339  
   340  	if len(comma) != 0 {
   341  		addComma(comma, &buffer, parts[0])
   342  	} else {
   343  		buffer.WriteString(parts[0])
   344  	}
   345  
   346  	//According to the precision to process the decimal parts
   347  	position, err := strconv.ParseUint(precision, 10, 64)
   348  	if err != nil {
   349  		return "", err
   350  	}
   351  
   352  	if position > 0 {
   353  		buffer.Write(decimalPoint)
   354  		if len(parts) == 2 {
   355  			if uint64(len(parts[1])) == position {
   356  				buffer.WriteString(parts[1][:position])
   357  			} else if uint64(len(parts[1])) > position {
   358  				//need to cast decimal's length
   359  				if parts[1][position:position+1] >= "5" {
   360  					buffer.Reset()
   361  					floatVar, err := strconv.ParseFloat(parts[0]+"."+parts[1], 64)
   362  					if err != nil {
   363  						return "", moerr.NewInvalidArgNoCtx("format parser error", parts[0]+"."+parts[1])
   364  					}
   365  					newNumber := strconv.FormatFloat(floatVar, 'f', int(position), 64)
   366  					newParts := strings.Split(newNumber, ".")
   367  
   368  					if len(comma) != 0 {
   369  						addComma(comma, &buffer, newParts[0])
   370  					} else {
   371  						buffer.WriteString(newParts[0])
   372  					}
   373  
   374  					buffer.Write(decimalPoint)
   375  					buffer.WriteString(newParts[1])
   376  				} else {
   377  					buffer.WriteString(parts[1][:position])
   378  				}
   379  			} else {
   380  				buffer.WriteString(parts[1])
   381  				buffer.WriteString(strings.Repeat("0", int(position)-len(parts[1])))
   382  			}
   383  		} else {
   384  			buffer.WriteString(strings.Repeat("0", int(position)))
   385  		}
   386  	}
   387  
   388  	return buffer.String(), nil
   389  }
   390  
   391  func addComma(comma []byte, buffer *bytes.Buffer, formatString string) {
   392  	pos := 0
   393  
   394  	//Add foramt comma for Integr parts
   395  	//If the integer part's length larger than 3
   396  	if len(formatString)%3 != 0 {
   397  		pos += len(formatString) % 3
   398  		buffer.WriteString(formatString[:pos])
   399  		buffer.Write(comma)
   400  	}
   401  	//Add a format comma every three digits
   402  	for ; pos < len(formatString); pos += 3 {
   403  		buffer.WriteString(formatString[pos : pos+3])
   404  		buffer.Write(comma)
   405  	}
   406  
   407  	buffer.Truncate(buffer.Len() - 1)
   408  
   409  }