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 for i := 0; i < v1.NumField(); i++ { 66 v1 := v1.Field(i) 67 v2 := v2.Field(i) 68 69 switch kind := v1.Kind(); kind { 70 case reflect.Interface, reflect.Ptr, reflect.Struct: 71 if !typeEqual(v1, v2) { 72 return false 73 } 74 } 75 } 76 77 return true 78} 79 80// Returns true if v recursively contains no interfaces 81func concreteType(v reflect.Value) bool { 82 if v.Kind() == reflect.Interface { 83 return false 84 } 85 86 if v.Kind() == reflect.Ptr { 87 if v.IsNil() { 88 return true 89 } 90 v = v.Elem() 91 } 92 93 if v.Kind() != reflect.Struct { 94 return true 95 } 96 97 for i := 0; i < v.NumField(); i++ { 98 v := v.Field(i) 99 100 switch kind := v.Kind(); kind { 101 case reflect.Interface, reflect.Ptr, reflect.Struct: 102 if !concreteType(v) { 103 return false 104 } 105 } 106 } 107 108 return true 109} 110