github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/queryparams/convert.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors 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 queryparams 18 19 import ( 20 "fmt" 21 "net/url" 22 "reflect" 23 "strings" 24 ) 25 26 // Marshaler converts an object to a query parameter string representation 27 type Marshaler interface { 28 MarshalQueryParameter() (string, error) 29 } 30 31 // Unmarshaler converts a string representation to an object 32 type Unmarshaler interface { 33 UnmarshalQueryParameter(string) error 34 } 35 36 func jsonTag(field reflect.StructField) (string, bool) { 37 structTag := field.Tag.Get("json") 38 if len(structTag) == 0 { 39 return "", false 40 } 41 parts := strings.Split(structTag, ",") 42 tag := parts[0] 43 if tag == "-" { 44 tag = "" 45 } 46 omitempty := false 47 parts = parts[1:] 48 for _, part := range parts { 49 if part == "omitempty" { 50 omitempty = true 51 break 52 } 53 } 54 return tag, omitempty 55 } 56 57 func formatValue(value interface{}) string { 58 return fmt.Sprintf("%v", value) 59 } 60 61 func isPointerKind(kind reflect.Kind) bool { 62 return kind == reflect.Ptr 63 } 64 65 func isStructKind(kind reflect.Kind) bool { 66 return kind == reflect.Struct 67 } 68 69 func isValueKind(kind reflect.Kind) bool { 70 switch kind { 71 case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, 72 reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, 73 reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, 74 reflect.Float64, reflect.Complex64, reflect.Complex128: 75 return true 76 default: 77 return false 78 } 79 } 80 81 func zeroValue(value reflect.Value) bool { 82 return reflect.DeepEqual(reflect.Zero(value.Type()).Interface(), value.Interface()) 83 } 84 85 func customMarshalValue(value reflect.Value) (reflect.Value, bool) { 86 // Return unless we implement a custom query marshaler 87 if !value.CanInterface() { 88 return reflect.Value{}, false 89 } 90 91 marshaler, ok := value.Interface().(Marshaler) 92 if !ok { 93 return reflect.Value{}, false 94 } 95 96 // Don't invoke functions on nil pointers 97 // If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response 98 if isPointerKind(value.Kind()) && zeroValue(value) { 99 return reflect.ValueOf(""), true 100 } 101 102 // Get the custom marshalled value 103 v, err := marshaler.MarshalQueryParameter() 104 if err != nil { 105 return reflect.Value{}, false 106 } 107 return reflect.ValueOf(v), true 108 } 109 110 func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) { 111 if omitempty && zeroValue(value) { 112 return 113 } 114 val := "" 115 iValue := fmt.Sprintf("%v", value.Interface()) 116 117 if iValue != "<nil>" { 118 val = iValue 119 } 120 values.Add(tag, val) 121 } 122 123 func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) { 124 for i := 0; i < list.Len(); i++ { 125 addParam(values, tag, omitempty, list.Index(i)) 126 } 127 } 128 129 // Convert takes an object and converts it to a url.Values object using JSON tags as 130 // parameter names. Only top-level simple values, arrays, and slices are serialized. 131 // Embedded structs, maps, etc. will not be serialized. 132 func Convert(obj interface{}) (url.Values, error) { 133 result := url.Values{} 134 if obj == nil { 135 return result, nil 136 } 137 var sv reflect.Value 138 switch reflect.TypeOf(obj).Kind() { 139 case reflect.Ptr, reflect.Interface: 140 sv = reflect.ValueOf(obj).Elem() 141 default: 142 return nil, fmt.Errorf("expecting a pointer or interface") 143 } 144 st := sv.Type() 145 if !isStructKind(st.Kind()) { 146 return nil, fmt.Errorf("expecting a pointer to a struct") 147 } 148 149 // Check all object fields 150 convertStruct(result, st, sv) 151 152 return result, nil 153 } 154 155 func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) { 156 for i := 0; i < st.NumField(); i++ { 157 field := sv.Field(i) 158 tag, omitempty := jsonTag(st.Field(i)) 159 if len(tag) == 0 { 160 continue 161 } 162 ft := field.Type() 163 164 kind := ft.Kind() 165 if isPointerKind(kind) { 166 ft = ft.Elem() 167 kind = ft.Kind() 168 if !field.IsNil() { 169 field = reflect.Indirect(field) 170 } 171 } 172 173 switch { 174 case isValueKind(kind): 175 addParam(result, tag, omitempty, field) 176 case kind == reflect.Array || kind == reflect.Slice: 177 if isValueKind(ft.Elem().Kind()) { 178 addListOfParams(result, tag, omitempty, field) 179 } 180 case isStructKind(kind) && !(zeroValue(field) && omitempty): 181 if marshalValue, ok := customMarshalValue(field); ok { 182 addParam(result, tag, omitempty, marshalValue) 183 } else { 184 convertStruct(result, ft, field) 185 } 186 } 187 } 188 }