class InitializrMetadataBuilder {

	private final List<InitializrMetadataCustomizer> customizers = new ArrayList<>();

	private final InitializrConfiguration configuration;

	private InitializrMetadataBuilder(InitializrConfiguration configuration) {
		this.configuration = configuration;
	}

	/**
	 * Add a {@link InitializrProperties} to be merged with other content. Merges the
	 * settings only and not the configuration.
	 * @param properties the properties to use
	 * @return this instance
	 * @see #withInitializrProperties(InitializrProperties, boolean)
	 */
	public InitializrMetadataBuilder withInitializrProperties(InitializrProperties properties) {
		return withInitializrProperties(properties, false);
	}

	/**
	 * Add a {@link InitializrProperties} to be merged with other content.
	 * @param properties the settings to merge onto this instance
	 * @param mergeConfiguration specify if service configuration should be merged as well
	 * @return this instance
	 */
	public InitializrMetadataBuilder withInitializrProperties(InitializrProperties properties,
			boolean mergeConfiguration) {
		if (mergeConfiguration) {
			this.configuration.merge(properties);
		}
		return withCustomizer(new InitializerPropertiesCustomizer(properties));
	}

	/**
	 * Add a {@link InitializrMetadata} to be merged with other content.
	 * @param resource a resource to a json document describing the metadata to include
	 * @return this instance
	 */
	public InitializrMetadataBuilder withInitializrMetadata(Resource resource) {
		return withCustomizer(new ResourceInitializrMetadataCustomizer(resource));
	}

	/**
	 * Add a {@link InitializrMetadataCustomizer}. customizers are invoked in their order
	 * of addition.
	 * @param customizer the customizer to add
	 * @return this instance
	 * @see InitializrMetadataCustomizer
	 */
	public InitializrMetadataBuilder withCustomizer(InitializrMetadataCustomizer customizer) {
		this.customizers.add(customizer);
		return this;
	}

	/**
	 * Build a {@link InitializrMetadata} based on the state of this builder.
	 * @return a new {@link InitializrMetadata} instance
	 */
	public InitializrMetadata build() {<FILL_FUNCTION_BODY>}

	/**
	 * Creates an empty instance based on the specified {@link InitializrConfiguration}.
	 * @param configuration the configuration
	 * @return a new {@link InitializrMetadata} instance
	 */
	protected InitializrMetadata createInstance(InitializrConfiguration configuration) {
		return new InitializrMetadata(configuration);
	}

	/**
	 * Apply defaults to capabilities that have no value.
	 * @param metadata the initializr metadata
	 */
	protected void applyDefaults(InitializrMetadata metadata) {
		if (!StringUtils.hasText(metadata.getName().getContent())) {
			metadata.getName().setContent("demo");
		}
		if (!StringUtils.hasText(metadata.getDescription().getContent())) {
			metadata.getDescription().setContent("Demo project for Spring Boot");
		}
		if (!StringUtils.hasText(metadata.getGroupId().getContent())) {
			metadata.getGroupId().setContent("com.example");
		}
		if (!StringUtils.hasText(metadata.getVersion().getContent())) {
			metadata.getVersion().setContent("0.0.1-SNAPSHOT");
		}
	}

	/**
	 * Create a builder instance from the specified {@link InitializrProperties}.
	 * Initialize the configuration to use.
	 * @param configuration the configuration to use
	 * @return a new {@link InitializrMetadataBuilder} instance
	 * @see #withInitializrProperties(InitializrProperties)
	 */
	public static InitializrMetadataBuilder fromInitializrProperties(InitializrProperties configuration) {
		return new InitializrMetadataBuilder(configuration).withInitializrProperties(configuration);
	}

	/**
	 * Create an empty builder instance with a default {@link InitializrConfiguration}.
	 * @return a new {@link InitializrMetadataBuilder} instance
	 */
	public static InitializrMetadataBuilder create() {
		return new InitializrMetadataBuilder(new InitializrConfiguration());
	}

	private static class InitializerPropertiesCustomizer implements InitializrMetadataCustomizer {

		private final InitializrProperties properties;

		InitializerPropertiesCustomizer(InitializrProperties properties) {
			this.properties = properties;
		}

		@Override
		public void customize(InitializrMetadata metadata) {
			metadata.getDependencies().merge(this.properties.getDependencies());
			metadata.getTypes().merge(this.properties.getTypes());
			metadata.getBootVersions().merge(this.properties.getBootVersions());
			metadata.getPackagings().merge(this.properties.getPackagings());
			metadata.getJavaVersions().merge(this.properties.getJavaVersions());
			metadata.getLanguages().merge(this.properties.getLanguages());
			this.properties.getGroupId().apply(metadata.getGroupId());
			this.properties.getArtifactId().apply(metadata.getArtifactId());
			this.properties.getVersion().apply(metadata.getVersion());
			this.properties.getName().apply(metadata.getName());
			this.properties.getDescription().apply(metadata.getDescription());
			this.properties.getPackageName().apply(metadata.getPackageName());
		}

	}

	private static class ResourceInitializrMetadataCustomizer implements InitializrMetadataCustomizer {

		private static final Log logger = LogFactory.getLog(ResourceInitializrMetadataCustomizer.class);

		private static final Charset UTF_8 = StandardCharsets.UTF_8;

		private final Resource resource;

		ResourceInitializrMetadataCustomizer(Resource resource) {
			this.resource = resource;
		}

		@Override
		public void customize(InitializrMetadata metadata) {
			logger.info("Loading initializr metadata from " + this.resource);
			try (InputStream in = this.resource.getInputStream()) {
				String content = StreamUtils.copyToString(in, UTF_8);
				ObjectMapper objectMapper = new ObjectMapper();
				InitializrMetadata anotherMetadata = objectMapper.readValue(content, InitializrMetadata.class);
				metadata.merge(anotherMetadata);
			}
			catch (Exception ex) {
				throw new IllegalStateException("Cannot merge", ex);
			}
		}

	}

}

		InitializrConfiguration config = (this.configuration != null) ? this.configuration
				: new InitializrConfiguration();
		InitializrMetadata metadata = createInstance(config);
		for (InitializrMetadataCustomizer customizer : this.customizers) {
			customizer.customize(metadata);
		}
		applyDefaults(metadata);
		metadata.validate();
		return metadata;
	
<no_super_class>