@@ -1021,21 +1021,34 @@ public static ResolvedMinBound resolveMinimumBound(OpenAPI openAPI, Schema<?> sc
10211021 }
10221022
10231023 /**
1024- * Returns the effective {@code default} for the given schema, resolving through a top-level
1025- * {@code $ref} and recursively walking any {@code allOf} items.
1026- * The inline schema's default takes precedence (explicit per-endpoint override);
1027- * falls back to the first non-null default found via depth-first search of {@code allOf} items.
1028- * Circular {@code allOf} references are detected and skipped.
1029- *
1030- * @param openAPI the OpenAPI document used to resolve {@code $ref}s
1031- * @param schema the schema to inspect
1032- * @return the effective default value, or {@code null} if none is defined
1033- */
1034- /**
1035- * Returns the effective {@code default} for the given schema, resolving through a top-level
1036- * {@code $ref} and recursively walking any {@code allOf} items.
1037- * The inline schema's default takes precedence (explicit per-endpoint override);
1038- * falls back to the first non-null default found via depth-first search of {@code allOf} items.
1024+ * Returns the effective {@code default} for the given schema using
1025+ * <em>last-writer-wins</em> semantics across the flattened {@code allOf} chain.
1026+ *
1027+ * <h3>Resolution algorithm</h3>
1028+ * <ol>
1029+ * <li>Resolve any top-level {@code $ref} to obtain the concrete schema.</li>
1030+ * <li>If the schema has a direct {@code default} (i.e. the {@code default:} key at
1031+ * the same level as {@code allOf:}), return it immediately — no traversal needed.</li>
1032+ * <li>Otherwise walk the {@code allOf} array <strong>top-to-bottom</strong>. Each
1033+ * item is itself fully resolved (recursing into nested {@code allOf} chains), and
1034+ * its result — if non-null — overwrites the current candidate (last-writer-wins).</li>
1035+ * </ol>
1036+ *
1037+ * <h3>Example</h3>
1038+ * <pre>{@code
1039+ * # Base1: default = "base_1" → Step 1: candidate = "base_1"
1040+ * # Base2: default = "base_2" → Step 2: candidate = "base_2" (overwrites Step 1)
1041+ * #
1042+ * # Intermediate:
1043+ * # allOf: [$ref Base1, $ref Base2]
1044+ * # → resolves to "base_2" (Base2 is last, wins over Base1)
1045+ * #
1046+ * # Final:
1047+ * # allOf:
1048+ * # - $ref: Intermediate → Step 3a: candidate = "base_2"
1049+ * # - default: "final" → Step 3b: candidate = "final" (overwrites Step 3a)
1050+ * # → resolves to "final"
1051+ * }</pre>
10391052 *
10401053 * @param openAPI the OpenAPI document used to resolve {@code $ref}s
10411054 * @param schema the schema to inspect
@@ -1045,27 +1058,34 @@ public static Object resolveDefault(OpenAPI openAPI, Schema<?> schema) {
10451058 schema = getReferencedSchema (openAPI , schema );
10461059 if (schema == null ) return null ;
10471060
1048- Object defaultValue = schema . getDefault ();
1049- if ( defaultValue != null ) {
1050- // inline default value takes precedence
1051- return defaultValue ;
1061+ // Direct default short-circuits — no allOf traversal needed.
1062+ Object directDefault = schema . getDefault ();
1063+ if ( directDefault != null ) {
1064+ return directDefault ;
10521065 }
1066+
1067+ // Walk allOf top-to-bottom; each non-null result overwrites the previous candidate.
10531068 if (hasAllOf (schema )) {
1054- return getFirstNonNullDefault (openAPI , schema ). orElse ( null );
1069+ return getLastNonNullDefault (openAPI , schema );
10551070 }
10561071 return null ;
10571072 }
10581073
10591074 /**
1060- * Recursively searches {@code allOf} items for the first non-null {@code default} value.
1061- * The first non-null default in {@code allOf} wins.
1062- * This behavior is arbitrary and may not reflect intentions of the spec creator, as default's inheritance/overriding behavior is undefined.
1075+ * Walks {@code allOf} items top-to-bottom and returns the <em>last</em> non-null
1076+ * {@code default} value found (last-writer-wins). Each item is fully resolved
1077+ * (including its own nested {@code allOf}) before the candidate is updated, so
1078+ * arbitrarily deep chains are flattened correctly.
10631079 */
1064- private static @ NonNull Optional <Object > getFirstNonNullDefault (OpenAPI openAPI , Schema <?> schema ) {
1065- return schema .getAllOf ().stream ()
1066- .map (item -> resolveDefault (openAPI , item ))
1067- .filter (Objects ::nonNull )
1068- .findFirst ();
1080+ private static Object getLastNonNullDefault (OpenAPI openAPI , Schema <?> schema ) {
1081+ Object last = null ;
1082+ for (Schema <?> item : schema .getAllOf ()) {
1083+ Object resolved = resolveDefault (openAPI , item );
1084+ if (resolved != null ) {
1085+ last = resolved ;
1086+ }
1087+ }
1088+ return last ;
10691089 }
10701090
10711091 public static boolean hasValidation (Schema sc ) {
0 commit comments