diff options
author | Colin Cross <ccross@android.com> | 2019-03-07 11:27:58 -0800 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-03-07 11:27:58 -0800 |
commit | 2cab480e587373be293104cb35ebff68d9ccea8b (patch) | |
tree | f3dc11476bb5e8fa9476c9bbed170ea900a970ab | |
parent | 8d7c363c122279d84ef179ddd31ce0a1cdf63525 (diff) | |
parent | fe4eb94eb07216b6ce65d5495bfa04875d0e18b9 (diff) | |
download | blueprint-2cab480e587373be293104cb35ebff68d9ccea8b.tar.gz |
Merge remote-tracking branch 'aosp/upstream' into master am: ba03162726
am: fe4eb94eb0
Change-Id: I3d1be2ddb41257175482d8042750be8d43f93bfb
-rw-r--r-- | Blueprints | 2 | ||||
-rw-r--r-- | proptools/proptools.go | 13 | ||||
-rw-r--r-- | proptools/tag.go | 67 | ||||
-rw-r--r-- | proptools/tag_test.go | 140 |
4 files changed, 209 insertions, 13 deletions
@@ -80,12 +80,14 @@ bootstrap_go_package { "proptools/escape.go", "proptools/extend.go", "proptools/proptools.go", + "proptools/tag.go", "proptools/typeequal.go", ], testSrcs: [ "proptools/clone_test.go", "proptools/escape_test.go", "proptools/extend_test.go", + "proptools/tag_test.go", "proptools/typeequal_test.go", ], } diff --git a/proptools/proptools.go b/proptools/proptools.go index f4da29e..e6e3ae7 100644 --- a/proptools/proptools.go +++ b/proptools/proptools.go @@ -15,8 +15,6 @@ package proptools import ( - "reflect" - "strings" "unicode" "unicode/utf8" ) @@ -39,17 +37,6 @@ func FieldNameForProperty(propertyName string) string { return fieldName } -func HasTag(field reflect.StructField, name, value string) bool { - tag := field.Tag.Get(name) - for _, entry := range strings.Split(tag, ",") { - if entry == value { - return true - } - } - - return false -} - // BoolPtr returns a pointer to a new bool containing the given value. func BoolPtr(b bool) *bool { return &b diff --git a/proptools/tag.go b/proptools/tag.go new file mode 100644 index 0000000..af5b97e --- /dev/null +++ b/proptools/tag.go @@ -0,0 +1,67 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "fmt" + "reflect" + "strings" +) + +// HasTag returns true if a StructField has a tag in the form `name:"foo,value"`. +func HasTag(field reflect.StructField, name, value string) bool { + tag := field.Tag.Get(name) + for _, entry := range strings.Split(tag, ",") { + if entry == value { + return true + } + } + + return false +} + +// PropertyIndexesWithTag returns the indexes of all properties (in the form used by reflect.Value.FieldByIndex) that +// are tagged with the given key and value, including ones found in embedded structs or pointers to structs. +func PropertyIndexesWithTag(ps interface{}, key, value string) [][]int { + t := reflect.TypeOf(ps) + if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct { + panic(fmt.Errorf("type %s is not a pointer to a struct", t)) + } + t = t.Elem() + + return propertyIndexesWithTag(t, key, value) +} +func propertyIndexesWithTag(t reflect.Type, key, value string) [][]int { + var indexes [][]int + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + ft := field.Type + if ft.Kind() == reflect.Struct || (ft.Kind() == reflect.Ptr && ft.Elem().Kind() == reflect.Struct) { + if ft.Kind() == reflect.Ptr { + ft = ft.Elem() + } + subIndexes := propertyIndexesWithTag(ft, key, value) + for _, sub := range subIndexes { + sub = append([]int{i}, sub...) + indexes = append(indexes, sub) + } + } else if HasTag(field, key, value) { + indexes = append(indexes, field.Index) + } + } + + return indexes +} diff --git a/proptools/tag_test.go b/proptools/tag_test.go new file mode 100644 index 0000000..0041c54 --- /dev/null +++ b/proptools/tag_test.go @@ -0,0 +1,140 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proptools + +import ( + "reflect" + "testing" +) + +func TestHasTag(t *testing.T) { + type testType struct { + NoTag string + EmptyTag string `` + OtherTag string `foo:"bar"` + MatchingTag string `name:"value"` + ExtraValues string `name:"foo,value,bar"` + ExtraTags string `foo:"bar" name:"value"` + } + + tests := []struct { + field string + want bool + }{ + { + field: "NoTag", + want: false, + }, + { + field: "EmptyTag", + want: false, + }, + { + field: "OtherTag", + want: false, + }, + { + field: "MatchingTag", + want: true, + }, + { + field: "ExtraValues", + want: true, + }, + { + field: "ExtraTags", + want: true, + }, + } + for _, test := range tests { + t.Run(test.field, func(t *testing.T) { + field, _ := reflect.TypeOf(testType{}).FieldByName(test.field) + if got := HasTag(field, "name", "value"); got != test.want { + t.Errorf(`HasTag(%q, "name", "value") = %v, want %v`, field.Tag, got, test.want) + } + }) + } +} + +func TestPropertyIndexesWithTag(t *testing.T) { + tests := []struct { + name string + ps interface{} + want [][]int + }{ + { + name: "none", + ps: &struct { + Foo string + }{}, + want: nil, + }, + { + name: "one", + ps: &struct { + Foo string `name:"value"` + }{}, + want: [][]int{{0}}, + }, + { + name: "two", + ps: &struct { + Foo string `name:"value"` + Bar string `name:"value"` + }{}, + want: [][]int{{0}, {1}}, + }, + { + name: "some", + ps: &struct { + Foo string `name:"other"` + Bar string `name:"value"` + }{}, + want: [][]int{{1}}, + }, + { + name: "embedded", + ps: &struct { + Foo struct { + Bar string `name:"value"` + } + }{}, + want: [][]int{{0, 0}}, + }, + { + name: "embedded ptr", + ps: &struct { + Foo *struct { + Bar string `name:"value"` + } + }{}, + want: [][]int{{0, 0}}, + }, + { + name: "nil", + ps: (*struct { + Foo string `name:"value"` + })(nil), + want: [][]int{{0}}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := PropertyIndexesWithTag(test.ps, "name", "value"); !reflect.DeepEqual(got, test.want) { + t.Errorf("PropertyIndexesWithTag() = %v, want %v", got, test.want) + } + }) + } +} |