1// Copyright 2015 Google Inc. All rights reserved. 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 15package proptools 16 17import "reflect" 18 19// TypeEqual takes two property structs, and returns true if they are of equal type, any embedded 20// pointers to structs or interfaces having matching nilitude, and any interface{} values in any 21// embedded structs, pointers to structs, or interfaces are also of equal type. 22func TypeEqual(s1, s2 interface{}) bool { 23 return typeEqual(reflect.ValueOf(s1), reflect.ValueOf(s2)) 24} 25 26func typeEqual(v1, v2 reflect.Value) bool { 27 if v1.Type() != v2.Type() { 28 return false 29 } 30 31 if v1.Kind() == reflect.Interface { 32 if v1.IsNil() != v2.IsNil() { 33 return false 34 } 35 if v1.IsNil() { 36 return true 37 } 38 v1 = v1.Elem() 39 v2 = v2.Elem() 40 if v1.Type() != v2.Type() { 41 return false 42 } 43 } 44 45 if v1.Kind() == reflect.Ptr { 46 if v1.Type().Elem().Kind() != reflect.Struct { 47 return true 48 } 49 if v1.IsNil() && !v2.IsNil() { 50 return concreteType(v2) 51 } else if v2.IsNil() && !v1.IsNil() { 52 return concreteType(v1) 53 } else if v1.IsNil() && v2.IsNil() { 54 return true 55 } 56 57 v1 = v1.Elem() 58 v2 = v2.Elem() 59 } 60 61 if v1.Kind() != reflect.Struct { 62 return true 63 } 64 65 if isConfigurable(v1.Type()) { 66 return true 67 } 68 69 for i := 0; i < v1.NumField(); i++ { 70 v1 := v1.Field(i) 71 v2 := v2.Field(i) 72 73 switch kind := v1.Kind(); kind { 74 case reflect.Interface, reflect.Ptr, reflect.Struct: 75 if !typeEqual(v1, v2) { 76 return false 77 } 78 } 79 } 80 81 return true 82} 83 84// Returns true if v recursively contains no interfaces 85func concreteType(v reflect.Value) bool { 86 if v.Kind() == reflect.Interface { 87 return false 88 } 89 90 if v.Kind() == reflect.Ptr { 91 if v.IsNil() { 92 return true 93 } 94 v = v.Elem() 95 } 96 97 if v.Kind() != reflect.Struct { 98 return true 99 } 100 101 if isConfigurable(v.Type()) { 102 return true 103 } 104 105 for i := 0; i < v.NumField(); i++ { 106 v := v.Field(i) 107 108 switch kind := v.Kind(); kind { 109 case reflect.Interface, reflect.Ptr, reflect.Struct: 110 if !concreteType(v) { 111 return false 112 } 113 } 114 } 115 116 return true 117} 118