diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java index 7128f3d56e2..dc3fd03136a 100644 --- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java +++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java @@ -193,7 +193,7 @@ public boolean isPreserveUnknownFields() { */ protected T internalFrom(TypeDef definition, String... ignore) { List schemaSwaps = new ArrayList<>(); - T ret = internalFromImpl(definition, new HashSet<>(), schemaSwaps, ignore); + T ret = internalFromImpl(definition, new HashSet<>(), schemaSwaps, new ParameterMap(new HashMap<>()), ignore); validateRemainingSchemaSwaps("unmatched class", schemaSwaps); return ret; } @@ -277,7 +277,7 @@ private void validateRemainingSchemaSwaps(String error, List } } - private T internalFromImpl(TypeDef definition, Set visited, List schemaSwaps, String... ignore) { + private T internalFromImpl(TypeDef definition, Set visited, List schemaSwaps, ParameterMap parameterMap, String... ignore) { final B builder = newBuilder(); Set ignores = ignore.length > 0 ? new LinkedHashSet<>(Arrays.asList(ignore)) : Collections @@ -312,7 +312,7 @@ private T internalFromImpl(TypeDef definition, Set visited, List matchedSchemaSwaps = facade.getMatchedSchemaSwaps(); currentSchemaSwaps.removeAll(matchedSchemaSwaps); @@ -324,7 +324,7 @@ private T internalFromImpl(TypeDef definition, Set visited, List indexPotentialAccessors(TypeDef definition) { return accessors; } + private static class ParameterMap { + final Map mappings; + + ParameterMap(Map mappings) { + this.mappings = mappings; + } + + TypeRef exchange(TypeRef original){ + return exchange(original, true); + } + + TypeRef exchange(TypeRef original, boolean throwOnFailedLookup){ + if (original instanceof TypeParamRef) { + String name = ((TypeParamRef) original).getName(); + TypeRef ref = mappings.get(name); + if(ref != null) { + return ref; + } + if(throwOnFailedLookup) { + throw new RuntimeException(String.format("Could not find type mapping for parametrized type %s", name)); + } + } + return original; + } + + static ParameterMap from(ClassRef classRef, ParameterMap parentMappings) { + TypeDef def = Types.typeDefFrom(classRef); + + Map mappings = new HashMap<>(); + if(!def.getParameters().isEmpty()) { + for(int i=0; i annotations; @@ -515,6 +556,7 @@ private static class PropertyFacade { private final List propertyOrAccessors = new ArrayList<>(4); private final Set schemaSwaps; private final Set matchedSchemaSwaps; + private final ParameterMap parameterMap; private String renamedTo; private String description; private String defaultValue; @@ -530,9 +572,10 @@ private static class PropertyFacade { private String descriptionContributedBy; private TypeRef schemaFrom; - public PropertyFacade(Property property, Map potentialAccessors, Set schemaSwaps) { + public PropertyFacade(Property property, Map potentialAccessors, Set schemaSwaps, ParameterMap parameterMap) { original = property; this.schemaSwaps = schemaSwaps; + this.parameterMap = parameterMap; this.matchedSchemaSwaps = new HashSet<>(); final String capitalized = property.getNameCapitalized(); final String name = property.getName(); @@ -610,7 +653,7 @@ public Property process() { } }); - TypeRef typeRef = schemaFrom != null ? schemaFrom : original.getTypeRef(); + TypeRef typeRef = schemaFrom != null ? schemaFrom : parameterMap.exchange(original.getTypeRef()); String finalName = renamedTo != null ? renamedTo : original.getName(); return new Property(original.getAnnotations(), typeRef, finalName, @@ -685,16 +728,16 @@ private String extractUpdatedNameFromJacksonPropertyIfPresent(Property property) * @return the structural schema associated with the specified property */ public T internalFrom(String name, TypeRef typeRef) { - return internalFromImpl(name, typeRef, new HashSet<>(), new ArrayList<>()); + return internalFromImpl(name, typeRef, new HashSet<>(), new ArrayList<>(), new ParameterMap(new HashMap<>())); } - private T internalFromImpl(String name, TypeRef typeRef, Set visited, List schemaSwaps) { + private T internalFromImpl(String name, TypeRef typeRef, Set visited, List schemaSwaps, ParameterMap parameterMap) { // Note that ordering of the checks here is meaningful: we need to check for complex types last // in case some "complex" types are handled specifically if (typeRef.getDimensions() > 0 || io.sundr.model.utils.Collections.isCollection(typeRef)) { // Handle Collections & Arrays final TypeRef collectionType = TypeAs.combine(TypeAs.UNWRAP_ARRAY_OF, TypeAs.UNWRAP_COLLECTION_OF) .apply(typeRef); - final T schema = internalFromImpl(name, collectionType, visited, schemaSwaps); + final T schema = internalFromImpl(name, parameterMap.exchange(collectionType), visited, schemaSwaps, parameterMap); return arrayLikeProperty(schema); } else if (io.sundr.model.utils.Collections.IS_MAP.apply(typeRef)) { // Handle Maps final TypeRef keyType = TypeAs.UNWRAP_MAP_KEY_OF.apply(typeRef); @@ -704,15 +747,15 @@ private T internalFromImpl(String name, TypeRef typeRef, Set visited, Li } final TypeRef valueType = TypeAs.UNWRAP_MAP_VALUE_OF.apply(typeRef); - T schema = internalFromImpl(name, valueType, visited, schemaSwaps); + T schema = internalFromImpl(name, parameterMap.exchange(valueType, false), visited, schemaSwaps, parameterMap); if (schema == null) { LOGGER.warn("Property '{}' with '{}' value type is mapped to 'object' because its CRD representation cannot be extracted.", name, typeRef); - schema = internalFromImpl(name, OBJECT_REF, visited, schemaSwaps); + schema = internalFromImpl(name, OBJECT_REF, visited, schemaSwaps, parameterMap); } return mapLikeProperty(schema); } else if (io.sundr.model.utils.Optionals.isOptional(typeRef)) { // Handle Optionals - return internalFromImpl(name, TypeAs.UNWRAP_OPTIONAL_OF.apply(typeRef), visited, schemaSwaps); + return internalFromImpl(name, parameterMap.exchange(TypeAs.UNWRAP_OPTIONAL_OF.apply(typeRef)), visited, schemaSwaps, parameterMap); } else { final String typeName = COMMON_MAPPINGS.get(typeRef); if (typeName != null) { // we have a type that we handle specifically @@ -724,9 +767,9 @@ private T internalFromImpl(String name, TypeRef typeRef, Set visited, Li } else { if (typeRef instanceof ClassRef) { // Handle complex types ClassRef classRef = (ClassRef) typeRef; - TypeDef def = Types.typeDefFrom(classRef); // check if we're dealing with an enum + TypeDef def = Types.typeDefFrom(classRef); if (def.isEnum()) { final JsonNode[] enumValues = def.getProperties().stream() .map(this::extractUpdatedNameFromJacksonPropertyIfPresent) @@ -735,7 +778,7 @@ private T internalFromImpl(String name, TypeRef typeRef, Set visited, Li .toArray(JsonNode[]::new); return enumProperty(enumValues); } else { - return resolveNestedClass(name, def, visited, schemaSwaps); + return resolveNestedClass(name, classRef, visited, schemaSwaps, parameterMap); } } @@ -747,7 +790,8 @@ private T internalFromImpl(String name, TypeRef typeRef, Set visited, Li // Flag to detect cycles private boolean resolving = false; - private T resolveNestedClass(String name, TypeDef def, Set visited, List schemaSwaps) { + private T resolveNestedClass(String name, ClassRef classRef, Set visited, List schemaSwaps, ParameterMap parameterMap) { + TypeDef def = Types.typeDefFrom(classRef); if (!resolving) { visited.clear(); resolving = true; @@ -759,7 +803,7 @@ private T resolveNestedClass(String name, TypeDef def, Set visited, List visited.add(visitedName); } - T res = internalFromImpl(def, visited, schemaSwaps); + T res = internalFromImpl(def, visited, schemaSwaps, ParameterMap.from(classRef, parameterMap)); resolving = false; return res; }