Allow pruning of unsupported fields in structs in maps

Adds support for traversing into a field that is of type:
   map[...]*struct{...}

This is needed to allow java_sdk_library to mark scope specific
properties, e.g. public.annotations as being target build release
specific.

It was necessary to change the Scope field from:
   Scope map[*apiScope]scopeProperties
to:
   Scope map[*apiScope]*scopeProperties

That is because there is no way in go to change the field of a struct
value of a map. i.e. you cannot do the following, not even using
reflection:
   Scope[apiScopePublic].AnnotationsZip = nil

Bug: 204763318
Test: m nothing
Change-Id: Id103f70f55d4202971321ef4925cbec4b55f8136
diff --git a/sdk/build_release.go b/sdk/build_release.go
index 212582a..2bcdc6f 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -269,6 +269,51 @@
 					subNamePrefix = name + "."
 				}
 				p.gatherFields(fieldType, fieldGetter, subNamePrefix, selector)
+
+			case reflect.Map:
+				// Get the type of the values stored in the map.
+				valueType := fieldType.Elem()
+				// Skip over * types.
+				if valueType.Kind() == reflect.Ptr {
+					valueType = valueType.Elem()
+				}
+				if valueType.Kind() == reflect.Struct {
+					// If this is not referenced by a pointer then it is an error as it is impossible to
+					// modify a struct that is stored directly as a value in a map.
+					if fieldType.Elem().Kind() != reflect.Ptr {
+						panic(fmt.Errorf("Cannot prune struct %s stored by value in map %s, map values must"+
+							" be pointers to structs",
+							fieldType.Elem(), name))
+					}
+
+					// Create a new pruner for the values of the map.
+					valuePruner := newPropertyPrunerForStructType(valueType, selector)
+
+					// Create a new fieldPruner that will iterate over all the items in the map and call the
+					// pruner on them.
+					fieldPruner := func(container reflect.Value) {
+						mapValue := fieldGetter(container)
+
+						for _, keyValue := range mapValue.MapKeys() {
+							itemValue := mapValue.MapIndex(keyValue)
+
+							defer func() {
+								if r := recover(); r != nil {
+									panic(fmt.Errorf("%s\n\tfor key %q", r, keyValue))
+								}
+							}()
+
+							valuePruner.pruneProperties(itemValue.Interface())
+						}
+					}
+
+					// Add the map field pruner to the list of property pruners.
+					property := prunerProperty{
+						name + "[*]",
+						fieldPruner,
+					}
+					p.properties = append(p.properties, property)
+				}
 			}
 		}
 	}
@@ -304,6 +349,13 @@
 // of properties.
 func newPropertyPruner(propertiesStruct interface{}, selector fieldSelectorFunc) *propertyPruner {
 	structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
+	return newPropertyPrunerForStructType(structType, selector)
+}
+
+// newPropertyPruner creates a new property pruner for the supplied properties struct type.
+//
+// The returned pruner can be used on any properties structure of the supplied type.
+func newPropertyPrunerForStructType(structType reflect.Type, selector fieldSelectorFunc) *propertyPruner {
 	pruner := &propertyPruner{}
 	pruner.gatherFields(structType, nil, "", selector)
 	return pruner