github.com/aldelo/common@v1.5.1/helper-reflect.go (about) 1 package helper 2 3 import ( 4 "database/sql" 5 "fmt" 6 "log" 7 "reflect" 8 "time" 9 ) 10 11 /* 12 * Copyright 2020-2023 Aldelo, LP 13 * 14 * Licensed under the Apache License, Version 2.0 (the "License"); 15 * you may not use this file except in compliance with the License. 16 * You may obtain a copy of the License at 17 * 18 * http://www.apache.org/licenses/LICENSE-2.0 19 * 20 * Unless required by applicable law or agreed to in writing, software 21 * distributed under the License is distributed on an "AS IS" BASIS, 22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 * See the License for the specific language governing permissions and 24 * limitations under the License. 25 */ 26 27 // ================================================================================================================ 28 // Custom Type Registry 29 // ================================================================================================================ 30 var customTypeRegistry map[string]reflect.Type 31 32 // ReflectTypeRegistryAdd will accept a custom struct object, and add its type into custom type registry, 33 // if customFullTypeName is not specified, the type name is inferred from the type itself, 34 // custom type registry is used by reflect unmarshal helpers to construct custom type for undefined interface targets 35 func ReflectTypeRegistryAdd(customStructObj interface{}, customFullTypeName ...string) bool { 36 if customStructObj == nil { 37 return false 38 } 39 40 o := reflect.TypeOf(customStructObj) 41 42 if o.Kind() == reflect.Ptr { 43 o = o.Elem() 44 } 45 46 if o.Kind() != reflect.Struct { 47 return false 48 } 49 50 typeName := o.Name() 51 log.Println(typeName) 52 53 if len(customFullTypeName) > 0 { 54 if LenTrim(customFullTypeName[0]) > 0 { 55 typeName = Trim(customFullTypeName[0]) 56 } 57 } 58 59 if customTypeRegistry == nil { 60 customTypeRegistry = make(map[string]reflect.Type) 61 } 62 63 customTypeRegistry[typeName] = o 64 return true 65 } 66 67 // ReflectTypeRegistryRemove will remove a pre-registered custom type from type registry for the given type name 68 func ReflectTypeRegistryRemove(customFullTypeName string) { 69 if customTypeRegistry != nil { 70 delete(customTypeRegistry, customFullTypeName) 71 } 72 } 73 74 // ReflectTypeRegistryRemoveAll will clear all previously registered custom types from type registry 75 func ReflectTypeRegistryRemoveAll() { 76 if customTypeRegistry != nil { 77 customTypeRegistry = make(map[string]reflect.Type) 78 } 79 } 80 81 // ReflectTypeRegistryCount returns count of custom types registered in the type registry 82 func ReflectTypeRegistryCount() int { 83 if customTypeRegistry != nil { 84 return len(customTypeRegistry) 85 } else { 86 return 0 87 } 88 } 89 90 // ReflectTypeRegistryGet returns a previously registered custom type in the type registry, based on the given type name string 91 func ReflectTypeRegistryGet(customFullTypeName string) reflect.Type { 92 if customTypeRegistry != nil { 93 if t, ok := customTypeRegistry[customFullTypeName]; ok { 94 return t 95 } else { 96 return nil 97 } 98 } else { 99 return nil 100 } 101 } 102 103 // ================================================================================================================ 104 // Custom Struct Tag Reflect Helpers 105 // ================================================================================================================ 106 107 // GetStructTagValueByObject will accept a struct object, struct field name, and struct tag name, 108 // and return the found tag value and reflect type, 109 // if reflect type or struct tag is not found, a notFound is returned 110 // [ Parameters ] 111 // 112 // structObj = struct object variable 113 // structFieldName = struct's field name (CASE SENSITIVE) 114 // structTagName = struct's tag name (the left side of struct tag - the key portion) (CASE SENSITIVE) 115 func GetStructTagValueByObject(structObj interface{}, structFieldName string, structTagName string) (notFound bool, tagValue string, t reflect.Type) { 116 // get reflect type from struct object 117 t = reflect.TypeOf(structObj) 118 119 if t == nil { 120 // no reflect type found 121 return true, "", nil 122 } 123 124 // get field 125 field, ok := t.FieldByName(structFieldName) 126 127 if !ok { 128 // struct field not found 129 return true, "", t 130 } else { 131 // struct field found 132 return false, field.Tag.Get(structTagName), t 133 } 134 } 135 136 // GetStructTagValueByType will accept a prior obtained reflect type, struct field name, and struct tag name, 137 // and return the found tag value, 138 // if struct tag value is not found, a notFound is returned, 139 // if the reflect type is nil, then not found is returned too 140 // [ Parameters ] 141 // 142 // t = reflect type of a struct object (obtained via GetStructTagValueByObject) 143 // structFieldName = struct's field name (CASE SENSITIVE) 144 // structTagName = struct's tag name (the left side of struct tag - the key portion) (CASE SENSITIVE) 145 func GetStructTagValueByType(t reflect.Type, structFieldName string, structTagName string) (notFound bool, tagValue string) { 146 // check if reflect type is valid 147 if t == nil { 148 return true, "" 149 } 150 151 // get field 152 field, ok := t.FieldByName(structFieldName) 153 154 if !ok { 155 // struct field not found 156 return true, "" 157 } else { 158 // struct field found 159 return false, field.Tag.Get(structTagName) 160 } 161 } 162 163 // GetStructTagsValueSlice returns named struct tag values from field, in the order queried 164 func GetStructTagsValueSlice(field reflect.StructField, tagName ...string) (tagValues []string) { 165 for _, t := range tagName { 166 tagValues = append(tagValues, field.Tag.Get(t)) 167 } 168 169 return 170 } 171 172 // ================================================================================================================ 173 // Reflection Helpers 174 // ================================================================================================================ 175 176 // ReflectCall uses reflection to invoke a method by name, and pass in param values if any, 177 // result is returned via reflect.Value object slice 178 func ReflectCall(o reflect.Value, methodName string, paramValue ...interface{}) (resultSlice []reflect.Value, notFound bool) { 179 method := o.MethodByName(methodName) 180 181 if method.Kind() == reflect.Invalid { 182 return nil, true 183 } 184 185 if !method.IsZero() { 186 var params []reflect.Value 187 188 if len(paramValue) > 0 { 189 for _, p := range paramValue { 190 params = append(params, reflect.ValueOf(p)) 191 } 192 } 193 194 resultSlice = method.Call(params) 195 196 if len(resultSlice) == 0 { 197 return nil, false 198 } else { 199 return resultSlice, false 200 } 201 } else { 202 return nil, true 203 } 204 } 205 206 // ReflectValueToString accepts reflect.Value and returns its underlying field value in string data type 207 // boolTrue is the literal value to use for bool true condition, boolFalse is the false condition literal, 208 // if boolTrue or boolFalse is not defined, then default 'true' or 'false' is used, 209 // skipBlank and skipZero if true indicates if field value is blank (string) or Zero (int, float, time, pointer, bool) then skip render, 210 // zeroBlank = will blank the value if it is 0, 0.00, or time.IsZero 211 // 212 // timeFormat: 213 // 214 // 2006, 06 = year, 215 // 01, 1, Jan, January = month, 216 // 02, 2, _2 = day (_2 = width two, right justified) 217 // 03, 3, 15 = hour (15 = 24 hour format) 218 // 04, 4 = minute 219 // 05, 5 = second 220 // PM pm = AM PM 221 func ReflectValueToString(o reflect.Value, boolTrue string, boolFalse string, skipBlank bool, skipZero bool, timeFormat string, zeroBlank bool) (valueStr string, skip bool, err error) { 222 buf := "" 223 224 switch o.Kind() { 225 case reflect.String: 226 buf = o.String() 227 228 if skipBlank && LenTrim(buf) == 0 { 229 return "", true, nil 230 } 231 case reflect.Bool: 232 if o.Bool() { 233 if len(boolTrue) == 0 { 234 buf = "true" 235 } else { 236 buf = Trim(boolTrue) 237 } 238 } else { 239 if skipZero { 240 return "", true, nil 241 } else { 242 if len(boolFalse) == 0 { 243 buf = "false" 244 } else { 245 if Trim(boolTrue) == Trim(boolFalse) { 246 buf = "false" 247 } else { 248 buf = Trim(boolFalse) 249 } 250 } 251 } 252 } 253 case reflect.Int8: 254 fallthrough 255 case reflect.Int16: 256 fallthrough 257 case reflect.Int: 258 fallthrough 259 case reflect.Int32: 260 fallthrough 261 case reflect.Int64: 262 if skipZero && o.Int() == 0 { 263 return "", true, nil 264 } else { 265 if zeroBlank && o.Int() == 0 { 266 buf = "" 267 } else { 268 buf = Int64ToString(o.Int()) 269 } 270 } 271 case reflect.Float32: 272 fallthrough 273 case reflect.Float64: 274 if skipZero && o.Float() == 0.00 { 275 return "", true, nil 276 } else { 277 if zeroBlank && o.Float() == 0.00 { 278 buf = "" 279 } else { 280 buf = FloatToString(o.Float()) 281 } 282 } 283 case reflect.Uint8: 284 fallthrough 285 case reflect.Uint16: 286 fallthrough 287 case reflect.Uint: 288 fallthrough 289 case reflect.Uint32: 290 fallthrough 291 case reflect.Uint64: 292 if skipZero && o.Uint() == 0 { 293 return "", true, nil 294 } else { 295 if zeroBlank && o.Uint() == 0 { 296 buf = "" 297 } else { 298 buf = UInt64ToString(o.Uint()) 299 } 300 } 301 case reflect.Ptr: 302 if o.IsZero() || o.IsNil() { 303 if skipZero || skipBlank { 304 return "", true, nil 305 } else { 306 if rt, _, _ := DerefPointersZero(o); rt.Kind() == reflect.Bool { 307 if Trim(boolTrue) == Trim(boolFalse) { 308 return "false", false, nil 309 } else { 310 if LenTrim(boolFalse) > 0 { 311 return boolFalse, false, nil 312 } else { 313 return "", false, nil 314 } 315 } 316 } else { 317 return "", false, nil 318 } 319 } 320 } 321 322 o2 := o.Elem() 323 324 if o2.IsZero() { 325 if skipZero || skipBlank { 326 return "", true, nil 327 } 328 } 329 330 switch f := o2.Interface().(type) { 331 case int8: 332 if skipZero && f == 0 { 333 return "", true, nil 334 } else { 335 if zeroBlank && f == 0 { 336 buf = "" 337 } else { 338 buf = Itoa(int(f)) 339 } 340 } 341 case int16: 342 if skipZero && f == 0 { 343 return "", true, nil 344 } else { 345 if zeroBlank && f == 0 { 346 buf = "" 347 } else { 348 buf = Itoa(int(f)) 349 } 350 } 351 case int32: 352 if skipZero && f == 0 { 353 return "", true, nil 354 } else { 355 if zeroBlank && f == 0 { 356 buf = "" 357 } else { 358 buf = Itoa(int(f)) 359 } 360 } 361 case int64: 362 if skipZero && f == 0 { 363 return "", true, nil 364 } else { 365 if zeroBlank && f == 0 { 366 buf = "" 367 } else { 368 buf = Int64ToString(f) 369 } 370 } 371 case int: 372 if skipZero && f == 0 { 373 return "", true, nil 374 } else { 375 if zeroBlank && f == 0 { 376 buf = "" 377 } else { 378 buf = Itoa(f) 379 } 380 } 381 case bool: 382 if f { 383 if len(boolTrue) == 0 { 384 buf = "true" 385 } else { 386 buf = Trim(boolTrue) 387 } 388 } else { 389 if skipZero { 390 return "", true, nil 391 } else { 392 if len(boolFalse) == 0 { 393 buf = "false" 394 } else { 395 if Trim(boolTrue) == Trim(boolFalse) { 396 buf = "false" 397 } else { 398 buf = Trim(boolFalse) 399 } 400 } 401 } 402 } 403 case string: 404 if skipBlank && LenTrim(f) == 0 { 405 return "", true, nil 406 } else { 407 buf = f 408 } 409 case float32: 410 if skipZero && f == 0.00 { 411 return "", true, nil 412 } else { 413 if zeroBlank && f == 0.00 { 414 buf = "" 415 } else { 416 buf = Float32ToString(f) 417 } 418 } 419 case float64: 420 if skipZero && f == 0.00 { 421 return "", true, nil 422 } else { 423 if zeroBlank && f == 0.00 { 424 buf = "" 425 } else { 426 buf = Float64ToString(f) 427 } 428 } 429 case uint: 430 if skipZero && f == 0 { 431 return "", true, nil 432 } else { 433 if zeroBlank && f == 0.00 { 434 buf = "" 435 } else { 436 buf = UintToStr(f) 437 } 438 } 439 case uint64: 440 if skipZero && f == 0 { 441 return "", true, nil 442 } else { 443 if zeroBlank && f == 0.00 { 444 buf = "" 445 } else { 446 buf = UInt64ToString(f) 447 } 448 } 449 case time.Time: 450 if skipZero && f.IsZero() { 451 return "", true, nil 452 } else { 453 if LenTrim(timeFormat) == 0 { 454 if zeroBlank && f.IsZero() { 455 buf = "" 456 } else { 457 buf = FormatDateTime(f) 458 } 459 } else { 460 if zeroBlank && f.IsZero() { 461 buf = "" 462 } else { 463 buf = f.Format(timeFormat) 464 } 465 } 466 } 467 default: 468 return "", false, fmt.Errorf("%s Unhandled [1]", o2.Type().Name()) 469 } 470 default: 471 switch f := o.Interface().(type) { 472 case sql.NullString: 473 buf = FromNullString(f) 474 475 if skipBlank && LenTrim(buf) == 0 { 476 return "", true, nil 477 } 478 case sql.NullBool: 479 if FromNullBool(f) { 480 if len(boolTrue) == 0 { 481 buf = "true" 482 } else { 483 buf = Trim(boolTrue) 484 } 485 } else { 486 if skipZero { 487 return "", true, nil 488 } else { 489 if len(boolFalse) == 0 { 490 buf = "false" 491 } else { 492 if Trim(boolTrue) == Trim(boolFalse) { 493 buf = "false" 494 } else { 495 buf = Trim(boolFalse) 496 } 497 } 498 } 499 } 500 case sql.NullFloat64: 501 f64 := FromNullFloat64(f) 502 503 if skipZero && f64 == 0.00 { 504 return "", true, nil 505 } else { 506 if zeroBlank && f64 == 0.00 { 507 buf = "" 508 } else { 509 buf = FloatToString(f64) 510 } 511 } 512 case sql.NullInt32: 513 i32 := FromNullInt(f) 514 515 if skipZero && i32 == 0 { 516 return "", true, nil 517 } else { 518 if zeroBlank && i32 == 0 { 519 buf = "" 520 } else { 521 buf = Itoa(i32) 522 } 523 } 524 case sql.NullInt64: 525 i64 := FromNullInt64(f) 526 527 if skipZero && i64 == 0 { 528 return "", true, nil 529 } else { 530 if zeroBlank && i64 == 0 { 531 buf = "" 532 } else { 533 buf = Int64ToString(i64) 534 } 535 } 536 case sql.NullTime: 537 t := FromNullTime(f) 538 539 if skipZero && t.IsZero() { 540 return "", true, nil 541 } else { 542 if LenTrim(timeFormat) == 0 { 543 buf = FormatDateTime(t) 544 } else { 545 if zeroBlank && t.IsZero() { 546 buf = "" 547 } else { 548 buf = t.Format(timeFormat) 549 } 550 } 551 } 552 case time.Time: 553 if skipZero && f.IsZero() { 554 return "", true, nil 555 } else { 556 if LenTrim(timeFormat) == 0 { 557 if zeroBlank && f.IsZero() { 558 buf = "" 559 } else { 560 buf = FormatDateTime(f) 561 } 562 } else { 563 if zeroBlank && f.IsZero() { 564 buf = "" 565 } else { 566 buf = f.Format(timeFormat) 567 } 568 } 569 } 570 case nil: 571 if skipZero || skipBlank { 572 return "", true, nil 573 } else { 574 buf = "" 575 } 576 default: 577 return "", false, fmt.Errorf("%s Unhandled [2]", o.Type().Name()) 578 } 579 } 580 581 return buf, false, nil 582 } 583 584 // ReflectStringToField accepts string value and reflects into reflect.Value field based on the field data type 585 // 586 // timeFormat: 587 // 588 // 2006, 06 = year, 589 // 01, 1, Jan, January = month, 590 // 02, 2, _2 = day (_2 = width two, right justified) 591 // 03, 3, 15 = hour (15 = 24 hour format) 592 // 04, 4 = minute 593 // 05, 5 = second 594 // PM pm = AM PM 595 func ReflectStringToField(o reflect.Value, v string, timeFormat string) error { 596 switch o.Kind() { 597 case reflect.String: 598 o.SetString(v) 599 case reflect.Bool: 600 b, _ := ParseBool(v) 601 o.SetBool(b) 602 case reflect.Int8: 603 fallthrough 604 case reflect.Int16: 605 fallthrough 606 case reflect.Int: 607 fallthrough 608 case reflect.Int32: 609 fallthrough 610 case reflect.Int64: 611 i64, _ := ParseInt64(v) 612 if !o.OverflowInt(i64) { 613 o.SetInt(i64) 614 } 615 case reflect.Float32: 616 fallthrough 617 case reflect.Float64: 618 f64, _ := ParseFloat64(v) 619 if !o.OverflowFloat(f64) { 620 o.SetFloat(f64) 621 } 622 case reflect.Uint8: 623 fallthrough 624 case reflect.Uint16: 625 fallthrough 626 case reflect.Uint: 627 fallthrough 628 case reflect.Uint32: 629 fallthrough 630 case reflect.Uint64: 631 ui64 := StrToUint64(v) 632 if !o.OverflowUint(ui64) { 633 o.SetUint(ui64) 634 } 635 case reflect.Ptr: 636 if o.IsZero() || o.IsNil() { 637 // create object 638 baseType, _, _ := DerefPointersZero(o) 639 o.Set(reflect.New(baseType.Type())) 640 } 641 642 o2 := o.Elem() 643 644 if o.IsZero() { 645 return nil 646 } 647 648 switch o2.Interface().(type) { 649 case int: 650 i64, _ := ParseInt64(v) 651 if !o2.OverflowInt(i64) { 652 o2.SetInt(i64) 653 } 654 case int8: 655 i64, _ := ParseInt64(v) 656 if !o2.OverflowInt(i64) { 657 o2.SetInt(i64) 658 } 659 case int16: 660 i64, _ := ParseInt64(v) 661 if !o2.OverflowInt(i64) { 662 o2.SetInt(i64) 663 } 664 case int32: 665 i64, _ := ParseInt64(v) 666 if !o2.OverflowInt(i64) { 667 o2.SetInt(i64) 668 } 669 case int64: 670 i64, _ := ParseInt64(v) 671 if !o2.OverflowInt(i64) { 672 o2.SetInt(i64) 673 } 674 case float32: 675 f64, _ := ParseFloat64(v) 676 if !o2.OverflowFloat(f64) { 677 o2.SetFloat(f64) 678 } 679 case float64: 680 f64, _ := ParseFloat64(v) 681 if !o2.OverflowFloat(f64) { 682 o2.SetFloat(f64) 683 } 684 case uint: 685 if !o2.OverflowUint(StrToUint64(v)) { 686 o2.SetUint(StrToUint64(v)) 687 } 688 case uint64: 689 if !o2.OverflowUint(StrToUint64(v)) { 690 o2.SetUint(StrToUint64(v)) 691 } 692 case string: 693 o2.SetString(v) 694 case bool: 695 b, _ := ParseBool(v) 696 o2.SetBool(b) 697 case time.Time: 698 if LenTrim(timeFormat) == 0 { 699 o2.Set(reflect.ValueOf(ParseDate(v))) 700 } else { 701 o2.Set(reflect.ValueOf(ParseDateTimeCustom(v, timeFormat))) 702 } 703 default: 704 return fmt.Errorf(o2.Type().Name() + " Unhandled [1]") 705 } 706 default: 707 switch o.Interface().(type) { 708 case sql.NullString: 709 o.Set(reflect.ValueOf(sql.NullString{String: v, Valid: true})) 710 case sql.NullBool: 711 b, _ := ParseBool(v) 712 o.Set(reflect.ValueOf(sql.NullBool{Bool: b, Valid: true})) 713 case sql.NullFloat64: 714 f64, _ := ParseFloat64(v) 715 o.Set(reflect.ValueOf(sql.NullFloat64{Float64: f64, Valid: true})) 716 case sql.NullInt32: 717 i32, _ := ParseInt32(v) 718 o.Set(reflect.ValueOf(sql.NullInt32{Int32: int32(i32), Valid: true})) 719 case sql.NullInt64: 720 i64, _ := ParseInt64(v) 721 o.Set(reflect.ValueOf(sql.NullInt64{Int64: i64, Valid: true})) 722 case sql.NullTime: 723 var tv time.Time 724 725 if LenTrim(timeFormat) == 0 { 726 tv = ParseDateTime(v) 727 } else { 728 tv = ParseDateTimeCustom(v, timeFormat) 729 } 730 731 o.Set(reflect.ValueOf(sql.NullTime{Time: tv, Valid: true})) 732 case time.Time: 733 if LenTrim(timeFormat) == 0 { 734 o.Set(reflect.ValueOf(ParseDateTime(v))) 735 } else { 736 o.Set(reflect.ValueOf(ParseDateTimeCustom(v, timeFormat))) 737 } 738 case nil: 739 return nil 740 default: 741 return fmt.Errorf(o.Type().Name() + " Unhandled [2]") 742 } 743 } 744 745 return nil 746 } 747 748 // DerefPointersZero gets pointer base type 749 func DerefPointersZero(rv reflect.Value) (drv reflect.Value, isPtr bool, isNilPtr bool) { 750 for rv.Kind() == reflect.Ptr { 751 isPtr = true 752 if rv.IsNil() { 753 isNilPtr = true 754 rt := rv.Type().Elem() 755 for rt.Kind() == reflect.Ptr { 756 rt = rt.Elem() 757 } 758 drv = reflect.New(rt).Elem() 759 return 760 } 761 rv = rv.Elem() 762 } 763 drv = rv 764 return 765 } 766 767 // DerefError dereferences reflect.Value to error object if underlying type was error 768 func DerefError(v reflect.Value) error { 769 if e, ok := v.Interface().(error); ok { 770 // v is error, check if error exists 771 if e != nil { 772 return e 773 } 774 } 775 776 return nil 777 } 778 779 // ReflectGetType returns the type of obj interface{} passed in. 780 // if obj interface{} is a pointer, then its base type will be returned instead 781 func ReflectGetType(obj interface{}) reflect.Type { 782 if obj == nil { 783 return nil 784 } else { 785 t1 := reflect.TypeOf(obj) 786 787 if t1.Kind() == reflect.Ptr { 788 return t1.Elem() 789 } else { 790 return t1 791 } 792 } 793 } 794 795 // ReflectObjectNewPtr creates a new object ptr for the object type given at parameter. 796 // the return interface{} represents the actual object ptr created 797 func ReflectObjectNewPtr(objType reflect.Type) interface{} { 798 if objType == nil { 799 return nil 800 } else { 801 return reflect.New(objType).Interface() 802 } 803 }