From 814b326b7809c28133b3340e377d9f1db8243370 Mon Sep 17 00:00:00 2001
From: boekkooi-fresh <wbo@hellofresh.com>
Date: Wed, 12 Jun 2019 17:53:42 +0200
Subject: [PATCH] Only flatten a type once

This should resolve issue https://github.com/droyo/go-xml/issues/86
---
 xsdgen/xsdgen.go | 26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)

diff --git a/xsdgen/xsdgen.go b/xsdgen/xsdgen.go
index 878b6f8..08fcf15 100644
--- a/xsdgen/xsdgen.go
+++ b/xsdgen/xsdgen.go
@@ -110,7 +110,7 @@ func (c *Code) NameOf(name xml.Name) string {
 		return "NOTFOUND" + name.Local
 	}
 
-	switch b := c.cfg.flatten1(t, func(xsd.Type) {}, 0).(type) {
+	switch b := c.cfg.flatten1(t, map[xml.Name]xsd.Type{}, func(xsd.Type) {}, 0).(type) {
 	case xsd.Builtin:
 		return c.NameOf(b.Name())
 	}
@@ -368,6 +368,8 @@ func (cfg *Config) flatten(types map[xml.Name]xsd.Type) []xsd.Type {
 	push := func(t xsd.Type) {
 		result = append(result, t)
 	}
+
+	var flattenedTypes = map[xml.Name]xsd.Type{}
 	for _, t := range types {
 		if xsd.XMLName(t).Local == "_self" {
 			continue
@@ -381,7 +383,7 @@ func (cfg *Config) flatten(types map[xml.Name]xsd.Type) []xsd.Type {
 				continue
 			}
 		}
-		if t := cfg.flatten1(t, push, 0); t != nil {
+		if t := cfg.flatten1(t, flattenedTypes, push, 0); t != nil {
 			push(t)
 		}
 	}
@@ -399,7 +401,12 @@ func dedup(types []xsd.Type) (unique []xsd.Type) {
 	return unique
 }
 
-func (cfg *Config) flatten1(t xsd.Type, push func(xsd.Type), depth int) xsd.Type {
+func (cfg *Config) flatten1(t xsd.Type, flattenedTypes map[xml.Name]xsd.Type, push func(xsd.Type), depth int) xsd.Type {
+	if res, ok := flattenedTypes[xsd.XMLName(t)]; ok {
+		return res
+	}
+	flattenedTypes[xsd.XMLName(t)] = t
+
 	const maxDepth = 1000
 	if depth > maxDepth {
 		return t
@@ -460,6 +467,8 @@ func (cfg *Config) flatten1(t xsd.Type, push func(xsd.Type), depth int) xsd.Type
 			t.Doc = "Must be at least " + strconv.Itoa(t.Restriction.MinLength) + " items long"
 			return t
 		}
+
+		flattenedTypes[xsd.XMLName(t)] = t.Base
 		return t.Base
 	case *xsd.ComplexType:
 		// We can "unpack" a struct if it is extending a simple
@@ -475,30 +484,33 @@ func (cfg *Config) flatten1(t xsd.Type, push func(xsd.Type), depth int) xsd.Type
 					t.Name.Local, xsd.XMLName(t.Base))
 				switch b := t.Base.(type) {
 				case xsd.Builtin:
+					flattenedTypes[xsd.XMLName(t)] = b
 					return b
 				case *xsd.SimpleType:
-					return cfg.flatten1(t.Base, push, depth+1)
+					res := cfg.flatten1(t.Base, flattenedTypes, push, depth+1)
+					flattenedTypes[xsd.XMLName(t)] = res
+					return res
 				}
 			}
 		}
 		// We can flatten a struct field if its type does not
 		// need additional methods for unmarshalling.
 		for i, el := range t.Elements {
-			el.Type = cfg.flatten1(el.Type, push, depth+1)
+			el.Type = cfg.flatten1(el.Type, flattenedTypes, push, depth+1)
 			t.Elements[i] = el
 			push(el.Type)
 			cfg.debugf("element %s %T(%s): %v", el.Name.Local, t,
 				xsd.XMLName(t).Local, xsd.XMLName(el.Type))
 		}
 		for i, attr := range t.Attributes {
-			attr.Type = cfg.flatten1(attr.Type, push, depth+1)
+			attr.Type = cfg.flatten1(attr.Type, flattenedTypes, push, depth+1)
 			t.Attributes[i] = attr
 			push(attr.Type)
 			cfg.debugf("attribute %s %T(%s): %v", attr.Name.Local, t,
 				xsd.XMLName(t).Local, xsd.XMLName(attr.Type))
 		}
 
-		t.Base = cfg.flatten1(t.Base, push, depth+1)
+		t.Base = cfg.flatten1(t.Base, flattenedTypes, push, depth+1)
 
 		// We expand all complexTypes to merge all of the fields from
 		// their ancestors, so generated code has no dependencies