diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/examples/XmlExampleGenerator.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/examples/XmlExampleGenerator.java index 40df89507e39..b1896605cdfb 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/examples/XmlExampleGenerator.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/examples/XmlExampleGenerator.java @@ -8,6 +8,8 @@ import com.wordnik.swagger.models.properties.*; import java.text.SimpleDateFormat; import java.util.*; +import org.codehaus.plexus.util.StringUtils; + public class XmlExampleGenerator { public static String NEWLINE = "\n"; public static String TAG_START = "<"; @@ -16,6 +18,7 @@ public class XmlExampleGenerator { protected Map examples; protected SimpleDateFormat dtFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); protected SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + private static String EMPTY = ""; public XmlExampleGenerator(Map examples) { this.examples = examples; @@ -24,29 +27,36 @@ public class XmlExampleGenerator { } public String toXml(Property property) { - return toXml(null, property, 0); + return toXml(null, property, 0, Collections.emptySet()); } - protected String toXml(Model model, int indent) { + protected String toXml(Model model, int indent, Collection path) { if(model instanceof RefModel) { RefModel ref = (RefModel) model; Model actualModel = examples.get(ref.getSimpleRef()); if(actualModel instanceof ModelImpl) - return modelImplToXml((ModelImpl)actualModel, indent); + return modelImplToXml((ModelImpl) actualModel, indent, path); } else if(model instanceof ModelImpl) { - return modelImplToXml((ModelImpl)model, indent); + return modelImplToXml((ModelImpl) model, indent, path); } return null; } - protected String modelImplToXml(ModelImpl model, int indent) { + protected String modelImplToXml(ModelImpl model, int indent, Collection path) { + final String modelName = model.getName(); + if (path.contains(modelName)) { + return EMPTY; + } + final Set selfPath = new HashSet(path); + selfPath.add(modelName); + StringBuilder sb = new StringBuilder(); // attributes Map attributes = new LinkedHashMap(); Map elements = new LinkedHashMap(); - String name = model.getName(); + String name = modelName; String namespace; String prefix; Boolean wrapped; @@ -67,13 +77,17 @@ public class XmlExampleGenerator { sb.append(name); for(String pName : attributes.keySet()) { Property p = attributes.get(pName); - sb.append(" ").append(pName).append("=").append(quote(toXml(null, p, 0))); + sb.append(" ").append(pName).append("=").append(quote(toXml(null, p, 0, selfPath))); } sb.append(CLOSE_TAG); sb.append(NEWLINE); for(String pName : elements.keySet()) { Property p = elements.get(pName); - sb.append(toXml(pName, p, indent + 1)); + final String asXml = toXml(pName, p, indent + 1, selfPath); + if (StringUtils.isEmpty(asXml)) { + continue; + } + sb.append(asXml); sb.append(NEWLINE); } sb.append(indent(indent)).append(TAG_END).append(name).append(CLOSE_TAG); @@ -85,7 +99,7 @@ public class XmlExampleGenerator { return "\"" + string + "\""; } - protected String toXml(String name, Property property, int indent) { + protected String toXml(String name, Property property, int indent, Collection path) { if(property == null) { return ""; } @@ -98,12 +112,16 @@ public class XmlExampleGenerator { if(property.getXml() != null && property.getXml().getWrapped()) wrapped = true; if(wrapped) { + String prefix = EMPTY; if(name != null) { sb.append(indent(indent)); sb.append(openTag(name)); - sb.append(NEWLINE); + prefix = NEWLINE; + } + final String asXml = toXml(name, inner, indent + 1, path); + if (StringUtils.isNotEmpty(asXml)) { + sb.append(prefix).append(asXml); } - sb.append(toXml(name, inner, indent + 1)); if(name != null) { sb.append(NEWLINE); sb.append(indent(indent)); @@ -111,12 +129,12 @@ public class XmlExampleGenerator { } } else - sb.append(toXml(name, inner, indent)); + sb.append(toXml(name, inner, indent, path)); } else if(property instanceof RefProperty) { RefProperty ref = (RefProperty) property; Model actualModel = examples.get(ref.getSimpleRef()); - sb.append(toXml(actualModel, indent)); + sb.append(toXml(actualModel, indent, path)); } else { if(name != null) { diff --git a/modules/swagger-codegen/src/test/scala/ExampleGeneratorTest.scala b/modules/swagger-codegen/src/test/scala/ExampleGeneratorTest.scala new file mode 100644 index 000000000000..5407202c5d33 --- /dev/null +++ b/modules/swagger-codegen/src/test/scala/ExampleGeneratorTest.scala @@ -0,0 +1,67 @@ +import scala.collection.JavaConverters.asScalaBufferConverter +import scala.collection.JavaConverters.mapAsJavaMapConverter +import scala.collection.JavaConverters.seqAsJavaListConverter + +import org.junit.runner.RunWith +import org.scalatest.FlatSpec +import org.scalatest.Matchers +import org.scalatest.junit.JUnitRunner + +import com.wordnik.swagger.codegen.examples.ExampleGenerator +import com.wordnik.swagger.models.Model +import com.wordnik.swagger.models.ModelImpl +import com.wordnik.swagger.models.Xml +import com.wordnik.swagger.models.properties.ArrayProperty +import com.wordnik.swagger.models.properties.RefProperty +import com.wordnik.swagger.models.properties.StringProperty + +@RunWith(classOf[JUnitRunner]) +class ExampleGeneratorTest extends FlatSpec with Matchers { + val json = "application/json" + val xml = "application/xml" + + it should "check handling of recursive models" in { + val nodeType = "Node" + val ref = new RefProperty(nodeType) + val node = new ModelImpl().name(nodeType).property("name", new StringProperty()) + node.property("parent", ref) + node.property("children", new ArrayProperty(ref)) + node.property("wrappedChildren", new ArrayProperty(ref).xml(new Xml().wrapped(true))) + val pairType = "Pair" + val pair = new ModelImpl().name(pairType) + for (item <- Map("first" -> "First", "second" -> "Second")) { + val property = new RefProperty(nodeType) + property.setXml(new Xml().name(item._2)) + pair.property(item._1, property); + } + val types = scala.collection.mutable.Buffer[String]() + val expectedTypes = List(json, xml) + val eg = new ExampleGenerator(Map[String, Model](nodeType -> node, pairType -> pair).asJava) + for (item <- eg.generate(null, expectedTypes.asJava, new RefProperty(pairType)).asScala) { + val example = item.get("example") + item.get("contentType") match { + case `xml` => { + types += xml + example should be ("\\n" + + " \\n" + + " string\\n" + + " \\n" + + " \\n" + + " \\n" + + " \\n" + + " string\\n" + + " \\n" + + " \\n" + + " \\n" + + "") + } + case `json` => { + types += json + // TODO - add JSON validation + example should not be (null) + } + } + } + types should be (expectedTypes) + } +}