1818package org .openapitools .codegen .languages ;
1919
2020import com .fasterxml .jackson .databind .JsonNode ;
21- import com .fasterxml .jackson .databind .ObjectMapper ;
2221import com .fasterxml .jackson .databind .node .ArrayNode ;
23- import com .fasterxml .jackson .databind .node .ObjectNode ;
2422import com .google .common .base .Strings ;
2523import com .google .common .collect .ImmutableMap ;
2624import com .google .common .collect .Sets ;
@@ -87,7 +85,6 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code
8785 private final Logger LOGGER = LoggerFactory .getLogger (AbstractJavaCodegen .class );
8886 private static final String ARTIFACT_VERSION_DEFAULT_VALUE = "1.0.0" ;
8987 private static final ZoneId UTC = ZoneId .of ("UTC" );
90-
9188 public static final String DEFAULT_LIBRARY = "<default>" ;
9289 public static final String DATE_LIBRARY = "dateLibrary" ;
9390 public static final String SUPPORT_ASYNC = "supportAsync" ;
@@ -1442,7 +1439,17 @@ public String toArrayDefaultValue(CodegenProperty cp, Schema schema) {
14421439
14431440 @ Override
14441441 public String toDefaultValue (CodegenProperty cp , Schema schema ) {
1445- schema = ModelUtils .getReferencedSchema (this .openAPI , schema );
1442+ Schema originalSchema = schema ;
1443+ Schema resolvedSchema = ModelUtils .getReferencedSchema (this .openAPI , schema );
1444+ if (hasOptionalNullableExplicitNullDefault (cp , originalSchema , resolvedSchema )) {
1445+ return null ;
1446+ }
1447+ String complexDefault = renderComplexDefaultValue (cp , originalSchema , resolvedSchema );
1448+ if (complexDefault != null ) {
1449+ return complexDefault ;
1450+ }
1451+
1452+ schema = resolvedSchema ;
14461453 if (ModelUtils .isArraySchema (schema )) {
14471454 if (defaultToEmptyContainer ) {
14481455 // if default to empty container option is set, respect the default values provided in the spec
@@ -1564,7 +1571,7 @@ public String toDefaultValue(CodegenProperty cp, Schema schema) {
15641571 return null ;
15651572 } else if (ModelUtils .isObjectSchema (schema )) {
15661573 if (schema .getDefault () != null ) {
1567- return toObjectDefaultValue (cp , schema . getDefault () , schema . getProperties () );
1574+ return renderComplexDefaultValue (cp , schema , schema );
15681575 }
15691576 return null ;
15701577 } else if (ModelUtils .isComposedSchema (schema )) {
@@ -1576,11 +1583,11 @@ public String toDefaultValue(CodegenProperty cp, Schema schema) {
15761583 // default (e.g. `{"one":"one"}`) as Java, which does not compile (see #23795).
15771584 Map <String , Schema > propertySchemas = getComposedSchemaProperties (schema );
15781585 if (!propertySchemas .isEmpty ()) {
1579- return toObjectDefaultValue (cp , schema . getDefault (), propertySchemas );
1586+ return renderComplexDefaultValue (cp , schema , schema );
15801587 }
15811588 // No object properties resolved: the composition wraps a non-object, e.g. an `allOf`
15821589 // to an enum or scalar (`allOf: [{$ref: '#/.../CurrencyCode'}]` + sibling `default`).
1583- // There is nothing to build via toObjectDefaultValue , so defer to the base behavior,
1590+ // There is nothing to build via the complex default renderer , so defer to the base behavior,
15841591 // which emits the raw default for later enum var-name / scalar conversion. Returning
15851592 // null here dropped the default and regressed enum defaults (see #24384).
15861593 return super .toDefaultValue (schema );
@@ -1591,102 +1598,33 @@ public String toDefaultValue(CodegenProperty cp, Schema schema) {
15911598 return super .toDefaultValue (schema );
15921599 }
15931600
1594- /**
1595- * Renders the default value of an object-typed property as a Java fluent builder expression, e.g.
1596- * {@code new Pet().name("doggie").id(1l)}. Only scalar (value node) default properties for which a
1597- * matching property schema is known are rendered; nested objects are skipped.
1598- *
1599- * @param cp the codegen property carrying the target Java type ({@code datatypeWithEnum})
1600- * @param defaultValue the raw default value from the schema (a {@code Map}/{@code ObjectNode})
1601- * @param propertySchemas the resolved property schemas used to type each default entry
1602- * @return the Java expression, or {@code null} if it cannot be resolved
1603- */
1604- private String toObjectDefaultValue (CodegenProperty cp , Object defaultValue , Map <String , Schema > propertySchemas ) {
1605- try {
1606- StringBuilder stringBuilder = new StringBuilder ();
1607- stringBuilder .append ("new " + cp .datatypeWithEnum + "()" );
1608- if (propertySchemas != null ) {
1609- // With `parseOptions.setResolve(true)`, objects with 1 key-value pair are LinkedHashMap and objects with more than 1 are ObjectNode
1610- // When not set, objects of any size are ObjectNode
1611- ObjectMapper objectMapper = new ObjectMapper ();
1612- ObjectNode objectNode ;
1613- if (!(defaultValue instanceof ObjectNode )) {
1614- objectNode = objectMapper .valueToTree (defaultValue );
1615- } else {
1616- objectNode = (ObjectNode ) defaultValue ;
1601+ private String renderComplexDefaultValue (CodegenProperty property , Schema schema , Schema resolvedSchema ) {
1602+ JavaDefaultValueRenderer .Context context = new JavaDefaultValueRenderer .Context (
1603+ openAPI ,
1604+ dateLibrary ,
1605+ useOneOfInterfaces ,
1606+ this ::escapeText ,
1607+ this ::toVarName ,
1608+ this ::toEnumVarName ,
1609+ this ::fromProperty ,
1610+ this ::getDefaultCollectionType ,
1611+ this ::getComposedSchemaProperties );
1612+ return new JavaDefaultValueRenderer (context ).render (property , schema , resolvedSchema );
1613+ }
16171614
1618- }
1619- Set <Map .Entry <String , JsonNode >> defaultProperties = objectNode .properties ();
1620- for (Map .Entry <String , JsonNode > defaultProperty : defaultProperties ) {
1621- String key = defaultProperty .getKey ();
1622- JsonNode value = defaultProperty .getValue ();
1623- Schema propertySchema = propertySchemas .get (key );
1624- if (!value .isValueNode () || propertySchema == null ) { //Skip complex objects for now
1625- continue ;
1626- }
1615+ private boolean hasOptionalNullableExplicitNullDefault (CodegenProperty property , Schema originalSchema ,
1616+ Schema resolvedSchema ) {
1617+ return openApiNullable && property != null && !property .required && property .isNullable
1618+ && isExplicitNullDefault (originalSchema , resolvedSchema );
1619+ }
16271620
1628- String defaultPropertyExpression = null ;
1629- if (ModelUtils .isEnumSchema (ModelUtils .getReferencedSchema (this .openAPI , propertySchema ))) {
1630- // Enum-typed property: render the enum constant (e.g. `OutputFormat.OrderEnum.SIMILARITY`)
1631- // rather than a raw quoted string, which would not compile (see #24298).
1632- CodegenProperty enumProperty = fromProperty (key , propertySchema );
1633- String enumType = enumProperty .isEnum
1634- // an inline enum is generated as a nested class of the containing object type
1635- ? cp .datatypeWithEnum + "." + enumProperty .datatypeWithEnum
1636- // a `$ref` to a named enum is a top-level type
1637- : enumProperty .datatypeWithEnum ;
1638- defaultPropertyExpression = enumType + "." + toEnumVarName (value .asText (), enumProperty .dataType );
1639- } else if (ModelUtils .isLongSchema (propertySchema )) {
1640- defaultPropertyExpression = value .asText ()+"l" ;
1641- } else if (ModelUtils .isIntegerSchema (propertySchema )) {
1642- defaultPropertyExpression = value .asText ();
1643- } else if (ModelUtils .isDoubleSchema (propertySchema )) {
1644- defaultPropertyExpression = value .asText ()+"d" ;
1645- } else if (ModelUtils .isFloatSchema (propertySchema )) {
1646- defaultPropertyExpression = value .asText ()+"f" ;
1647- } else if (ModelUtils .isNumberSchema (propertySchema )) {
1648- defaultPropertyExpression = "new java.math.BigDecimal(\" " + value .asText () + "\" )" ;
1649- } else if (ModelUtils .isURISchema (propertySchema )) {
1650- defaultPropertyExpression = "java.net.URI.create(\" " + escapeText (value .asText ()) + "\" )" ;
1651- } else if (ModelUtils .isDateSchema (propertySchema )) {
1652- if ("java8" .equals (getDateLibrary ())) {
1653- defaultPropertyExpression = String .format (Locale .ROOT , "java.time.LocalDate.parse(\" %s\" )" , value .asText ());
1654- }
1655- } else if (ModelUtils .isDateTimeSchema (propertySchema )) {
1656- if ("java8" .equals (getDateLibrary ())) {
1657- defaultPropertyExpression = String .format (Locale .ROOT , "java.time.OffsetDateTime.parse(\" %s\" , %s)" ,
1658- value .asText (),
1659- "java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(java.time.ZoneId.systemDefault())" );
1660- }
1661- } else if (ModelUtils .isTimeLocalSchema (propertySchema )) {
1662- if ("java8" .equals (getDateLibrary ())) {
1663- defaultPropertyExpression = String .format (Locale .ROOT , "java.time.LocalTime.parse(\" %s\" )" , value .asText ());
1664- }
1665- } else if (ModelUtils .isDateTimeLocalSchema (propertySchema )) {
1666- if ("java8" .equals (getDateLibrary ())) {
1667- defaultPropertyExpression = String .format (Locale .ROOT , "java.time.LocalDateTime.parse(\" %s\" )" , value .asText ());
1668- }
1669- } else if (ModelUtils .isUUIDSchema (propertySchema )) {
1670- defaultPropertyExpression = "java.util.UUID.fromString(\" " + value .asText () + "\" )" ;
1671- } else if (ModelUtils .isStringSchema (propertySchema )) {
1672- defaultPropertyExpression = "\" " + value .asText () + "\" " ;
1673- } else if (ModelUtils .isBooleanSchema (propertySchema )) {
1674- defaultPropertyExpression = value .asText ();
1675- }
1676- if (defaultPropertyExpression != null ) {
1677- stringBuilder
1678- // .append(System.lineSeparator())
1679- .append ("." )
1680- .append (toVarName (key ))
1681- .append ("(" ).append (defaultPropertyExpression ).append (")" );
1682- }
1683- }
1684- }
1685- return stringBuilder .toString ();
1686- } catch (ClassCastException e ) {
1687- LOGGER .error ("Can't resolve default value: " +defaultValue , e );
1688- return null ;
1689- }
1621+ private boolean isExplicitNullDefault (Schema originalSchema , Schema resolvedSchema ) {
1622+ return isNullNode (originalSchema == null ? null : originalSchema .getDefault ())
1623+ || isNullNode (resolvedSchema == null ? null : resolvedSchema .getDefault ());
1624+ }
1625+
1626+ private boolean isNullNode (Object defaultValue ) {
1627+ return defaultValue instanceof JsonNode && ((JsonNode ) defaultValue ).isNull ();
16901628 }
16911629
16921630 /**
@@ -1702,15 +1640,31 @@ private Map<String, Schema> getComposedSchemaProperties(Schema schema) {
17021640 if (schema .getProperties () != null ) {
17031641 propertySchemas .putAll (schema .getProperties ());
17041642 }
1705- if (schema .getAllOf () != null ) {
1706- for (Object member : schema .getAllOf ()) {
1707- Schema resolved = ModelUtils .getReferencedSchema (this .openAPI , (Schema ) member );
1708- if (resolved != null && resolved .getProperties () != null ) {
1709- propertySchemas .putAll (resolved .getProperties ());
1710- }
1643+ Set <Schema > visitedSchemas = Collections .newSetFromMap (new IdentityHashMap <>());
1644+ visitedSchemas .add (schema );
1645+ addComposedSchemaProperties (propertySchemas , schema .getAllOf (), visitedSchemas );
1646+ addComposedSchemaProperties (propertySchemas , schema .getOneOf (), visitedSchemas );
1647+ addComposedSchemaProperties (propertySchemas , schema .getAnyOf (), visitedSchemas );
1648+ return propertySchemas ;
1649+ }
1650+
1651+ private void addComposedSchemaProperties (Map <String , Schema > propertySchemas , List <Schema > members ,
1652+ Set <Schema > visitedSchemas ) {
1653+ if (members == null ) {
1654+ return ;
1655+ }
1656+ for (Schema member : members ) {
1657+ Schema resolved = ModelUtils .getReferencedSchema (this .openAPI , member );
1658+ if (resolved == null || !visitedSchemas .add (resolved )) {
1659+ continue ;
17111660 }
1661+ if (resolved .getProperties () != null ) {
1662+ propertySchemas .putAll (resolved .getProperties ());
1663+ }
1664+ addComposedSchemaProperties (propertySchemas , resolved .getAllOf (), visitedSchemas );
1665+ addComposedSchemaProperties (propertySchemas , resolved .getOneOf (), visitedSchemas );
1666+ addComposedSchemaProperties (propertySchemas , resolved .getAnyOf (), visitedSchemas );
17121667 }
1713- return propertySchemas ;
17141668 }
17151669
17161670 private String getDefaultCollectionType (Schema schema ) {
0 commit comments