@@ -646,7 +646,7 @@ protected void normalizeComponentsSchemas() {
646646 schemas .put (schemaName , normalizeSchema (schema , new HashSet <>()));
647647
648648 if (getRule (REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING )) {
649- ensureInheritance (schema , schemaName );
649+ ensureInheritanceForDiscriminatorMappings (schema , schemaName );
650650 }
651651 }
652652 }
@@ -1585,85 +1585,201 @@ protected Schema processSimplifyOneOf(Schema schema) {
15851585 * Ensure inheritance is correctly defined for OneOf and Discriminators.
15861586 *
15871587 * For schemas containing oneOf and discriminator.propertyName:
1588- * Create the mappings as $refs
1589- * Remove OneOf
1590- *
1591- * For referenced schemas, ensure that there is an allOf with this schema.
1588+ * <ul>
1589+ * <li>Create the mappings as $refs</li>
1590+ * <li>Remove OneOf</li>
1591+ * </ul>
15921592 */
15931593 protected Schema processReplaceOneOfByMapping (Schema schema ) {
15941594 if (!getRule (REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING )) {
15951595 return schema ;
15961596 }
15971597 Discriminator discriminator = schema .getDiscriminator ();
15981598 if (discriminator != null ) {
1599- if (discriminator .getMapping () == null ) {
1599+ boolean inlineSchema = isInlineSchema (schema );
1600+ if (inlineSchema ) {
1601+ // the For referenced schemas, ensure that there is an allOf with this schema.
1602+ LOGGER .warn ("Inline oneOf schema not supported by REPLACE_ONE_OF_BY_DISCRIMINATOR_MAPPING normalization" );
1603+ return schema ;
1604+ }
1605+ if (discriminator .getMapping () == null && discriminator .getPropertyName () != null ) {
16001606 Map <String , String > mappings = new TreeMap <>();
16011607 discriminator .setMapping (mappings );
1602- for ( Object oneOfObject : schema .getOneOf ()) {
1603- Schema oneOf = ( Schema ) oneOfObject ;
1608+ List < Schema > oneOfs = schema .getOneOf ();
1609+ for ( Schema oneOf : oneOfs ) {
16041610 String refSchema = oneOf .get$ref ();
16051611 if (refSchema != null ) {
1606- String name = getDiscriminatorValue (refSchema );
1612+ boolean hasProperty = findProperty (schema , discriminator .getPropertyName (), false , new HashSet <>()) != null ;
1613+ String name = getDiscriminatorValue (refSchema , discriminator .getPropertyName (), hasProperty );
16071614 mappings .put (name , refSchema );
16081615 }
16091616 }
16101617 }
1611-
1612-
1618+ // remove oneOf and only keep the discriminator mapping
16131619 schema .setOneOf (null );
16141620 }
1621+
16151622 return schema ;
16161623 }
16171624
1618- protected String getDiscriminatorValue (String refSchema ) {
1619- String schemaName = refSchema .contains ("/" ) ? refSchema .substring (refSchema .lastIndexOf ('/' ) + 1 ) : refSchema ;;
1625+ private boolean isInlineSchema (Schema schema ) {
1626+ if (openAPI .getComponents ()!=null && openAPI .getComponents ().getSchemas ()!=null ) {
1627+ int identity = System .identityHashCode (schema );
1628+ for (Schema componentSchema : openAPI .getComponents ().getSchemas ().values ()) {
1629+ if (System .identityHashCode (componentSchema ) == identity ) {
1630+ return false ;
1631+ }
1632+ }
1633+ }
1634+ return true ;
1635+ }
1636+
1637+ /**
1638+ * Best effort to retrieve a good discriminator value.
1639+ * By order of precedence:
1640+ * <ul>
1641+ * <li>x-discriminator-value</li>
1642+ * <li>single enum value for attribute used by the discriminator.propertyName</li>
1643+ * <li>hame of the schema</li>
1644+ * </ul>
1645+ *
1646+ * @param refSchema $ref value like #/components/schemas/Dog
1647+ * @param discriminatorPropertyName name of the property used in the discriminator mapping
1648+ * @param propertyAlreadyPresent if true, delete the property in the referenced schemas to avoid duplicates
1649+ *
1650+ * @return the name
1651+ */
1652+ protected String getDiscriminatorValue (String refSchema , String discriminatorPropertyName , boolean propertyAlreadyPresent ) {
1653+ String schemaName = ModelUtils .getSimpleRef (refSchema );
16201654 Schema schema = ModelUtils .getSchema (openAPI , schemaName );
1655+ Schema property = findProperty (schema , discriminatorPropertyName , propertyAlreadyPresent , new HashSet <>());
16211656 if (schema != null && schema .getExtensions () != null ) {
16221657 Object discriminatorValue = schema .getExtensions ().get ("x-discriminator-value" );
16231658 if (discriminatorValue != null ) {
16241659 return discriminatorValue .toString ();
16251660 }
16261661 }
1662+
1663+ // find the discriminator value as a unique enum value
1664+ if (property != null ) {
1665+ List enums = property .getEnum ();
1666+ if (enums != null && enums .size () == 1 ) {
1667+ return enums .get (0 ).toString ();
1668+ }
1669+ }
1670+
16271671 return schemaName ;
16281672 }
16291673
1674+ /**
1675+ * find a property under the schema.
1676+ *
1677+ * @param schema
1678+ * @param propertyName property to find
1679+ * @param toDelete if true delete the found property
1680+ * @param visitedSchemas avoid infinite recursion
1681+ * @return found property or null if not found.
1682+ */
1683+ private Schema findProperty (Schema schema , String propertyName , boolean toDelete , HashSet <Object > visitedSchemas ) {
1684+ if (propertyName == null || schema == null || visitedSchemas .contains (schema )) {
1685+ return null ;
1686+ }
1687+ visitedSchemas .add (schema );
1688+ Map <String , Schema > properties = schema .getProperties ();
1689+ if (properties != null ) {
1690+ Schema property = properties .get (propertyName );
1691+ if (property != null ) {
1692+ if (toDelete ) {
1693+ if (schema .getProperties ().remove (propertyName ) != null ) {
1694+ schema .setProperties (null );
1695+ }
1696+ }
1697+ return property ;
1698+ }
1699+ }
1700+ List <Schema > allOfs = schema .getAllOf ();
1701+ if (allOfs != null ) {
1702+ for (Schema child : allOfs ) {
1703+ Schema found = findProperty (child , propertyName , toDelete , visitedSchemas );
1704+ if (found != null ) {
1705+ return found ;
1706+ }
1707+ }
1708+ }
1709+
1710+ return null ;
1711+ }
1712+
16301713
1631- protected void ensureInheritance (Schema parent , String parentName ) {
1714+ /**
1715+ * ensure that all schemas referenced in the discriminator mapping has an allOf to the parent schema.
1716+ *
1717+ * This allows DefaultCodeGen to detect inheritance.
1718+ *
1719+ * @param parent parent schma
1720+ * @param parentName name of the parent schema
1721+ */
1722+ protected void ensureInheritanceForDiscriminatorMappings (Schema parent , String parentName ) {
16321723 Discriminator discriminator = parent .getDiscriminator ();
16331724 if (discriminator != null && discriminator .getMapping () != null ) {
1634-
16351725 for (String mapping : discriminator .getMapping ().values ()) {
1636- String refSchemaName = getDiscriminatorValue (mapping );
1726+ String refSchemaName = ModelUtils . getSimpleRef (mapping );
16371727 Schema child = ModelUtils .getSchema (openAPI , refSchemaName );
16381728 if (child != null ) {
1639- ensureInheritance (parent , child , parentName );
1729+ if (parentName != null ) {
1730+ ensureInheritanceForDiscriminatorMappings (parent , child , parentName , new HashSet <>());
1731+ }
16401732 }
16411733 }
16421734 }
16431735 }
16441736
1645- protected void ensureInheritance (Schema parent , Schema child , String parentName ) {
1737+ /**
1738+ * If not already present, add in the child an allOf referencing the parent.
1739+ */
1740+ protected void ensureInheritanceForDiscriminatorMappings (Schema parent , Schema child , String parentName , Set <Schema > visitedSchemas ) {
16461741 String reference = "#/components/schemas/" + parentName ;
16471742 List <Schema > allOf = child .getAllOf ();
16481743 if (allOf != null ) {
1649- if (hasParent (child , parent , reference )) {
1744+ if (hasParent (parent , child , reference , visitedSchemas )) {
1745+ // already done, so no need to add
16501746 return ;
16511747 }
1748+ Schema refToParent = new Schema <>().$ref (reference );
1749+ allOf .add (refToParent );
16521750 } else {
16531751 allOf = new ArrayList <>();
16541752 child .setAllOf (allOf );
1753+ Schema refToParent = new Schema <>().$ref (reference );
1754+ allOf .add (refToParent );
1755+ if (child .getProperties () != null ) {
1756+ // move the properties inside the new allOf.
1757+ Schema childProperties = new Schema <>().properties (child .getProperties ());
1758+ allOf .add (childProperties );
1759+ child .setProperties (null );
1760+ child .setType (null );
1761+ }
16551762 }
1656- Schema refToParent = new Schema <>().$ref (reference );
1657- allOf .add (refToParent );
16581763 }
16591764
1660- private boolean hasParent (Schema child , Schema parent , String reference ) {
1765+ /**
1766+ * return true if the child as an allOf referencing the parent scham.
1767+ */
1768+ private boolean hasParent (Schema parent , Schema child , String reference , Set <Schema > visitedSchemas ) {
16611769 if (child .get$ref () != null && child .get$ref ().equals (reference )) {
16621770 return true ;
16631771 }
16641772 List <Schema > allOf = child .getAllOf ();
16651773 if (allOf != null ) {
1666- return allOf .stream ().anyMatch (s -> hasParent (s , parent , reference ));
1774+ for (Schema schema : allOf ) {
1775+ if (visitedSchemas .contains (schema )) {
1776+ return false ;
1777+ }
1778+ visitedSchemas .add (schema );
1779+ if (hasParent (schema , parent , reference , visitedSchemas )) {
1780+ return true ;
1781+ }
1782+ }
16671783 }
16681784 return false ;
16691785 }
0 commit comments