Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / apimachinery / pkg / conversion / queryparams / convert.go
1 /*
2 Copyright 2014 The Kubernetes Authors.
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                 if !isPointerKind(value.Kind()) && value.CanAddr() {
94                         marshaler, ok = value.Addr().Interface().(Marshaler)
95                         if !ok {
96                                 return reflect.Value{}, false
97                         }
98                 } else {
99                         return reflect.Value{}, false
100                 }
101         }
102
103         // Don't invoke functions on nil pointers
104         // If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response
105         if isPointerKind(value.Kind()) && zeroValue(value) {
106                 return reflect.ValueOf(""), true
107         }
108
109         // Get the custom marshalled value
110         v, err := marshaler.MarshalQueryParameter()
111         if err != nil {
112                 return reflect.Value{}, false
113         }
114         return reflect.ValueOf(v), true
115 }
116
117 func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) {
118         if omitempty && zeroValue(value) {
119                 return
120         }
121         val := ""
122         iValue := fmt.Sprintf("%v", value.Interface())
123
124         if iValue != "<nil>" {
125                 val = iValue
126         }
127         values.Add(tag, val)
128 }
129
130 func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) {
131         for i := 0; i < list.Len(); i++ {
132                 addParam(values, tag, omitempty, list.Index(i))
133         }
134 }
135
136 // Convert takes an object and converts it to a url.Values object using JSON tags as
137 // parameter names. Only top-level simple values, arrays, and slices are serialized.
138 // Embedded structs, maps, etc. will not be serialized.
139 func Convert(obj interface{}) (url.Values, error) {
140         result := url.Values{}
141         if obj == nil {
142                 return result, nil
143         }
144         var sv reflect.Value
145         switch reflect.TypeOf(obj).Kind() {
146         case reflect.Ptr, reflect.Interface:
147                 sv = reflect.ValueOf(obj).Elem()
148         default:
149                 return nil, fmt.Errorf("expecting a pointer or interface")
150         }
151         st := sv.Type()
152         if !isStructKind(st.Kind()) {
153                 return nil, fmt.Errorf("expecting a pointer to a struct")
154         }
155
156         // Check all object fields
157         convertStruct(result, st, sv)
158
159         return result, nil
160 }
161
162 func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) {
163         for i := 0; i < st.NumField(); i++ {
164                 field := sv.Field(i)
165                 tag, omitempty := jsonTag(st.Field(i))
166                 if len(tag) == 0 {
167                         continue
168                 }
169                 ft := field.Type()
170
171                 kind := ft.Kind()
172                 if isPointerKind(kind) {
173                         ft = ft.Elem()
174                         kind = ft.Kind()
175                         if !field.IsNil() {
176                                 field = reflect.Indirect(field)
177                                 // If the field is non-nil, it should be added to params
178                                 // and the omitempty should be overwite to false
179                                 omitempty = false
180                         }
181                 }
182
183                 switch {
184                 case isValueKind(kind):
185                         addParam(result, tag, omitempty, field)
186                 case kind == reflect.Array || kind == reflect.Slice:
187                         if isValueKind(ft.Elem().Kind()) {
188                                 addListOfParams(result, tag, omitempty, field)
189                         }
190                 case isStructKind(kind) && !(zeroValue(field) && omitempty):
191                         if marshalValue, ok := customMarshalValue(field); ok {
192                                 addParam(result, tag, omitempty, marshalValue)
193                         } else {
194                                 convertStruct(result, ft, field)
195                         }
196                 }
197         }
198 }