Merge pull request #28 from jgennis/local-variant

Add CreateLocalVariations to BottupUpMutatorContext.
diff --git a/proptools/proptools.go b/proptools/proptools.go
index 4868408..2a71ea1 100644
--- a/proptools/proptools.go
+++ b/proptools/proptools.go
@@ -146,3 +146,49 @@
 		}
 	}
 }
+
+func CloneEmptyProperties(structValue reflect.Value) reflect.Value {
+	result := reflect.New(structValue.Type())
+	cloneEmptyProperties(result.Elem(), structValue)
+	return result
+}
+
+func cloneEmptyProperties(dstValue, srcValue reflect.Value) {
+	typ := srcValue.Type()
+	for i := 0; i < srcValue.NumField(); i++ {
+		field := typ.Field(i)
+		if field.PkgPath != "" {
+			// The field is not exported so just skip it.
+			continue
+		}
+
+		srcFieldValue := srcValue.Field(i)
+		dstFieldValue := dstValue.Field(i)
+
+		switch srcFieldValue.Kind() {
+		case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint:
+			// Nothing
+		case reflect.Struct:
+			cloneEmptyProperties(dstFieldValue, srcFieldValue)
+		case reflect.Ptr, reflect.Interface:
+			if !srcFieldValue.IsNil() {
+				elem := srcFieldValue.Elem()
+				if srcFieldValue.Kind() == reflect.Interface {
+					if elem.Kind() != reflect.Ptr {
+						panic(fmt.Errorf("can't clone field %q: interface "+
+							"refers to a non-pointer", field.Name))
+					}
+					elem = elem.Elem()
+				}
+				if elem.Elem().Kind() != reflect.Struct {
+					panic(fmt.Errorf("can't clone field %q: points to a "+
+						"non-struct", field.Name))
+				}
+				dstFieldValue.Set(CloneEmptyProperties(elem.Elem()))
+			}
+		default:
+			panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
+				field.Name, srcFieldValue.Kind()))
+		}
+	}
+}