github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/util/struct.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  // Field is a field of an arbitrary struct
    29  type Field struct {
    30  	Name  string
    31  	Path  string
    32  	Type  reflect.Type
    33  	Kind  reflect.Kind
    34  	Leaf  bool
    35  	Depth int
    36  	Tag   reflect.StructTag
    37  	Value interface{}
    38  	Addr  interface{}
    39  	Hide  string
    40  }
    41  
    42  // ParseObj parses an object structure, calling back with field info
    43  // for each field
    44  func ParseObj(obj interface{}, cb func(*Field) error, tags map[string]string) error {
    45  	if cb == nil {
    46  		return errors.New("nil callback")
    47  	}
    48  	return parse(obj, cb, nil, tags)
    49  }
    50  
    51  func parse(ptr interface{}, cb func(*Field) error, parent *Field, tags map[string]string) error {
    52  	v := reflect.ValueOf(ptr)
    53  	err := parse2(v, cb, parent, tags)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	return nil
    58  }
    59  
    60  func parse2(val reflect.Value, cb func(*Field) error, parent *Field, tags map[string]string) error {
    61  	var path string
    62  	var depth int
    63  	v := val.Elem()
    64  	t := v.Type()
    65  	for i := 0; i < v.NumField(); i++ {
    66  		vf := v.Field(i)
    67  		tf := t.Field(i)
    68  		name := strings.ToLower(tf.Name)
    69  		if tf.Name[0] == name[0] {
    70  			continue // skip unexported fields
    71  		}
    72  		if parent != nil {
    73  			path = fmt.Sprintf("%s.%s", parent.Path, name)
    74  			depth = parent.Depth + 1
    75  		} else {
    76  			path = name
    77  		}
    78  		kind := vf.Kind()
    79  		leaf := kind != reflect.Struct && kind != reflect.Ptr
    80  		field := &Field{
    81  			Name:  name,
    82  			Path:  path,
    83  			Type:  tf.Type,
    84  			Kind:  kind,
    85  			Leaf:  leaf,
    86  			Depth: depth,
    87  			Tag:   tf.Tag,
    88  			Value: vf.Interface(),
    89  			Addr:  vf.Addr().Interface(),
    90  		}
    91  		if parent == nil || parent.Hide == "" {
    92  			getHideTag(field, tags)
    93  		} else {
    94  			field.Hide = parent.Hide
    95  		}
    96  		err := cb(field)
    97  		if err != nil {
    98  			return err
    99  		}
   100  		if kind == reflect.Ptr {
   101  			// Skip parsing the entire struct if "skip" tag is present on a struct field
   102  			if tf.Tag.Get(TagSkip) == "true" {
   103  				continue
   104  			}
   105  			rf := reflect.New(vf.Type().Elem())
   106  			err := parse2(rf, cb, field, tags)
   107  			if err != nil {
   108  				return err
   109  			}
   110  		} else if kind == reflect.Struct {
   111  			// Skip parsing the entire struct if "skip" tag is present on a struct field
   112  			if tf.Tag.Get(TagSkip) == "true" {
   113  				continue
   114  			}
   115  			err := parse(field.Addr, cb, field, tags)
   116  			if err != nil {
   117  				return err
   118  			}
   119  		}
   120  	}
   121  	return nil
   122  }
   123  
   124  // CopyMissingValues checks the dst interface for missing values and
   125  // replaces them with value from src config struct.
   126  // This does a deep copy of pointers.
   127  func CopyMissingValues(src, dst interface{}) {
   128  	s := reflect.ValueOf(src).Elem()
   129  	d := reflect.ValueOf(dst).Elem()
   130  	copyMissingValues(s, d)
   131  }
   132  
   133  func copyMissingValues(src, dst reflect.Value) {
   134  	if !src.IsValid() {
   135  		return
   136  	}
   137  	switch src.Kind() {
   138  	case reflect.Ptr:
   139  		src = src.Elem()
   140  		if !src.IsValid() {
   141  			return
   142  		}
   143  		if dst.IsNil() {
   144  			dst.Set(reflect.New(src.Type()))
   145  		}
   146  		copyMissingValues(src, dst.Elem())
   147  	case reflect.Interface:
   148  		if src.IsNil() {
   149  			return
   150  		}
   151  		src = src.Elem()
   152  		if dst.IsNil() {
   153  			newVal := reflect.New(src.Type()).Elem()
   154  			copyMissingValues(src, newVal)
   155  			dst.Set(newVal)
   156  		} else {
   157  			copyMissingValues(src, dst.Elem())
   158  		}
   159  	case reflect.Struct:
   160  		if !src.IsValid() {
   161  			return
   162  		}
   163  		t, ok := src.Interface().(time.Time)
   164  		if ok {
   165  			dst.Set(reflect.ValueOf(t))
   166  		}
   167  		for i := 0; i < src.NumField(); i++ {
   168  			copyMissingValues(src.Field(i), dst.Field(i))
   169  		}
   170  	case reflect.Slice:
   171  		if !dst.IsNil() {
   172  			return
   173  		}
   174  		dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap()))
   175  		for i := 0; i < src.Len(); i++ {
   176  			copyMissingValues(src.Index(i), dst.Index(i))
   177  		}
   178  	case reflect.Map:
   179  		if dst.IsNil() {
   180  			dst.Set(reflect.MakeMap(src.Type()))
   181  		}
   182  		for _, key := range src.MapKeys() {
   183  			sval := src.MapIndex(key)
   184  			dval := dst.MapIndex(key)
   185  			copy := !dval.IsValid()
   186  			if copy {
   187  				dval = reflect.New(sval.Type()).Elem()
   188  			}
   189  			copyMissingValues(sval, dval)
   190  			if copy {
   191  				dst.SetMapIndex(key, dval)
   192  			}
   193  		}
   194  	default:
   195  		if !dst.CanInterface() {
   196  			return
   197  		}
   198  		dval := dst.Interface()
   199  		zval := reflect.Zero(dst.Type()).Interface()
   200  		if reflect.DeepEqual(dval, zval) {
   201  			dst.Set(src)
   202  		}
   203  	}
   204  }
   205  
   206  func getHideTag(f *Field, tags map[string]string) {
   207  	var key, val string
   208  	key = fmt.Sprintf("%s.%s", "hide", f.Path)
   209  	if tags != nil {
   210  		val = tags[key]
   211  	}
   212  	if val == "" {
   213  		val = f.Tag.Get("hide")
   214  	}
   215  	f.Hide = val
   216  }