Home / Class/ MetadataGenerationEnvironment Class — spring-boot Architecture

MetadataGenerationEnvironment Class — spring-boot Architecture

Architecture documentation for the MetadataGenerationEnvironment class in MetadataGenerationEnvironment.java from the spring-boot codebase.

Entity Profile

Relationship Graph

Source Code

configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java lines 56–410

class MetadataGenerationEnvironment {

	private static final String NULLABLE_ANNOTATION = "org.jspecify.annotations.Nullable";

	private static final Set<String> TYPE_EXCLUDES = Set.of("com.zaxxer.hikari.IConnectionCustomizer",
			"groovy.lang.MetaClass", "groovy.text.markup.MarkupTemplateEngine", "java.io.Writer", "java.io.PrintWriter",
			"java.lang.ClassLoader", "java.util.concurrent.ThreadFactory", "jakarta.jms.XAConnectionFactory",
			"javax.sql.DataSource", "javax.sql.XADataSource", "org.apache.tomcat.jdbc.pool.PoolConfiguration",
			"org.apache.tomcat.jdbc.pool.Validator", "org.flywaydb.core.api.callback.FlywayCallback",
			"org.flywaydb.core.api.resolver.MigrationResolver");

	private static final Set<String> DEPRECATION_EXCLUDES = Set.of(
			"org.apache.commons.dbcp2.BasicDataSource#getPassword",
			"org.apache.commons.dbcp2.BasicDataSource#getUsername");

	private final TypeUtils typeUtils;

	private final Elements elements;

	private final Messager messager;

	private final FieldValuesParser fieldValuesParser;

	private final ConfigurationPropertiesSourceResolver sourceResolver;

	private final Map<TypeElement, Map<String, Object>> defaultValues = new HashMap<>();

	private final Map<TypeElement, SourceMetadata> sources = new HashMap<>();

	private final String configurationPropertiesAnnotation;

	private final String nestedConfigurationPropertyAnnotation;

	private final String configurationPropertiesSourceAnnotation;

	private final String deprecatedConfigurationPropertyAnnotation;

	private final String constructorBindingAnnotation;

	private final String defaultValueAnnotation;

	private final Set<String> endpointAnnotations;

	private final String readOperationAnnotation;

	private final String nameAnnotation;

	private final String autowiredAnnotation;

	MetadataGenerationEnvironment(ProcessingEnvironment environment, String configurationPropertiesAnnotation,
			String configurationPropertiesSourceAnnotation, String nestedConfigurationPropertyAnnotation,
			String deprecatedConfigurationPropertyAnnotation, String constructorBindingAnnotation,
			String autowiredAnnotation, String defaultValueAnnotation, Set<String> endpointAnnotations,
			String readOperationAnnotation, String nameAnnotation) {
		this.typeUtils = new TypeUtils(environment);
		this.elements = environment.getElementUtils();
		this.messager = environment.getMessager();
		this.fieldValuesParser = resolveFieldValuesParser(environment);
		this.sourceResolver = new ConfigurationPropertiesSourceResolver(environment, this.typeUtils);
		this.configurationPropertiesAnnotation = configurationPropertiesAnnotation;
		this.configurationPropertiesSourceAnnotation = configurationPropertiesSourceAnnotation;
		this.nestedConfigurationPropertyAnnotation = nestedConfigurationPropertyAnnotation;
		this.deprecatedConfigurationPropertyAnnotation = deprecatedConfigurationPropertyAnnotation;
		this.constructorBindingAnnotation = constructorBindingAnnotation;
		this.autowiredAnnotation = autowiredAnnotation;
		this.defaultValueAnnotation = defaultValueAnnotation;
		this.endpointAnnotations = endpointAnnotations;
		this.readOperationAnnotation = readOperationAnnotation;
		this.nameAnnotation = nameAnnotation;
	}

	private static FieldValuesParser resolveFieldValuesParser(ProcessingEnvironment env) {
		try {
			return new JavaCompilerFieldValuesParser(env);
		}
		catch (Throwable ex) {
			return FieldValuesParser.NONE;
		}
	}

	TypeUtils getTypeUtils() {
		return this.typeUtils;
	}

	Messager getMessager() {
		return this.messager;
	}

	/**
	 * Return the default value of the given {@code field}.
	 * @param type the type to consider
	 * @param field the field or {@code null} if it is not available
	 * @return the default value or {@code null} if the field does not exist or no default
	 * value has been detected
	 */
	Object getFieldDefaultValue(TypeElement type, VariableElement field) {
		return (field != null) ? this.defaultValues.computeIfAbsent(type, this::resolveFieldValues)
			.get(field.getSimpleName().toString()) : null;
	}

	/**
	 * Resolve the {@link SourceMetadata} for the specified property.
	 * @param field the field of the property (can be {@code null})
	 * @param getter the getter of the property (can be {@code null})
	 * @return the {@link SourceMetadata} for the specified property
	 */
	SourceMetadata resolveSourceMetadata(VariableElement field, ExecutableElement getter) {
		if (field != null && field.getEnclosingElement() instanceof TypeElement type) {
			return this.sources.computeIfAbsent(type, this.sourceResolver::resolveSource);
		}
		if (getter != null && getter.getEnclosingElement() instanceof TypeElement type) {
			return this.sources.computeIfAbsent(type, this.sourceResolver::resolveSource);
		}
		return SourceMetadata.EMPTY;
	}

	boolean isExcluded(TypeMirror type) {
		if (type == null) {
			return false;
		}
		String typeName = type.toString();
		if (typeName.endsWith("[]")) {
			typeName = typeName.substring(0, typeName.length() - 2);
		}
		return TYPE_EXCLUDES.contains(typeName);
	}

	boolean isDeprecated(Element element) {
		if (element == null) {
			return false;
		}
		String elementName = element.getEnclosingElement() + "#" + element.getSimpleName();
		if (DEPRECATION_EXCLUDES.contains(elementName)) {
			return false;
		}
		if (isElementDeprecated(element)) {
			return true;
		}
		if (element instanceof VariableElement || element instanceof ExecutableElement) {
			return isElementDeprecated(element.getEnclosingElement());
		}
		return false;
	}

	ItemDeprecation resolveItemDeprecation(Element element) {
		AnnotationMirror annotation = getAnnotation(element, this.deprecatedConfigurationPropertyAnnotation);
		String reason = null;
		String replacement = null;
		String since = null;
		if (annotation != null) {
			reason = getAnnotationElementStringValue(annotation, "reason");
			replacement = getAnnotationElementStringValue(annotation, "replacement");
			since = getAnnotationElementStringValue(annotation, "since");
		}
		return new ItemDeprecation(reason, replacement, since);
	}

	boolean hasConstructorBindingAnnotation(ExecutableElement element) {
		return hasAnnotation(element, this.constructorBindingAnnotation, true);
	}

	boolean hasAutowiredAnnotation(ExecutableElement element) {
		return hasAnnotation(element, this.autowiredAnnotation);
	}

	boolean hasAnnotation(Element element, String type) {
		return hasAnnotation(element, type, false);
	}

	boolean hasAnnotation(Element element, String type, boolean considerMetaAnnotations) {
		if (element != null) {
			for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
				if (type.equals(annotation.getAnnotationType().toString())) {
					return true;
				}
			}
			if (considerMetaAnnotations) {
				Set<Element> seen = new HashSet<>();
				for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
					if (hasMetaAnnotation(annotation.getAnnotationType().asElement(), type, seen)) {
						return true;
					}
				}

			}
		}
		return false;
	}

	private boolean hasMetaAnnotation(Element annotationElement, String type, Set<Element> seen) {
		if (seen.add(annotationElement)) {
			for (AnnotationMirror annotation : annotationElement.getAnnotationMirrors()) {
				DeclaredType annotationType = annotation.getAnnotationType();
				if (type.equals(annotationType.toString())
						|| hasMetaAnnotation(annotationType.asElement(), type, seen)) {
					return true;
				}
			}
		}
		return false;
	}

	AnnotationMirror getAnnotation(Element element, String type) {
		if (element != null) {
			for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
				if (type.equals(annotation.getAnnotationType().toString())) {
					return annotation;
				}
			}
		}
		return null;
	}

	private AnnotationMirror getTypeUseAnnotation(Element element, String type) {
		if (element != null) {
			for (AnnotationMirror annotation : element.asType().getAnnotationMirrors()) {
				if (type.equals(annotation.getAnnotationType().toString())) {
					return annotation;
				}
			}
		}
		return null;
	}

	/**
	 * Collect the annotations that are annotated or meta-annotated with the specified
	 * {@link TypeElement annotation}.
	 * @param element the element to inspect
	 * @param annotationType the annotation to discover
	 * @return the annotations that are annotated or meta-annotated with this annotation
	 */
	List<Element> getElementsAnnotatedOrMetaAnnotatedWith(Element element, TypeElement annotationType) {
		LinkedList<Element> stack = new LinkedList<>();
		stack.push(element);
		collectElementsAnnotatedOrMetaAnnotatedWith(annotationType, stack);
		stack.removeFirst();
		return Collections.unmodifiableList(stack);
	}

	private boolean collectElementsAnnotatedOrMetaAnnotatedWith(TypeElement annotationType, LinkedList<Element> stack) {
		Element element = stack.peekLast();
		for (AnnotationMirror annotation : this.elements.getAllAnnotationMirrors(element)) {
			Element annotationElement = annotation.getAnnotationType().asElement();
			if (!stack.contains(annotationElement)) {
				stack.addLast(annotationElement);
				if (annotationElement.equals(annotationType)) {
					return true;
				}
				if (!collectElementsAnnotatedOrMetaAnnotatedWith(annotationType, stack)) {
					stack.removeLast();
				}
			}
		}
		return false;
	}

	Map<String, Object> getAnnotationElementValues(AnnotationMirror annotation) {
		Map<String, Object> values = new LinkedHashMap<>();
		annotation.getElementValues()
			.forEach((name, value) -> values.put(name.getSimpleName().toString(), getAnnotationValue(value)));
		return values;
	}

	String getAnnotationElementStringValue(AnnotationMirror annotation, String name) {
		return annotation.getElementValues()
			.entrySet()
			.stream()
			.filter((element) -> element.getKey().getSimpleName().toString().equals(name))
			.map((element) -> asString(getAnnotationValue(element.getValue())))
			.findFirst()
			.orElse(null);
	}

	private Object getAnnotationValue(AnnotationValue annotationValue) {
		Object value = annotationValue.getValue();
		if (value instanceof List) {
			List<Object> values = new ArrayList<>();
			((List<?>) value).forEach((v) -> values.add(((AnnotationValue) v).getValue()));
			return values;
		}
		return value;
	}

	private String asString(Object value) {
		return (value == null || value.toString().isEmpty()) ? null : (String) value;
	}

	TypeElement getConfigurationPropertiesAnnotationElement() {
		return this.elements.getTypeElement(this.configurationPropertiesAnnotation);
	}

	AnnotationMirror getConfigurationPropertiesAnnotation(Element element) {
		return getAnnotation(element, this.configurationPropertiesAnnotation);
	}

	TypeElement getConfigurationPropertiesSourceAnnotationElement() {
		return this.elements.getTypeElement(this.configurationPropertiesSourceAnnotation);
	}

	AnnotationMirror getNestedConfigurationPropertyAnnotation(Element element) {
		return getAnnotation(element, this.nestedConfigurationPropertyAnnotation);
	}

	AnnotationMirror getDefaultValueAnnotation(Element element) {
		return getAnnotation(element, this.defaultValueAnnotation);
	}

	Set<TypeElement> getEndpointAnnotationElements() {
		return this.endpointAnnotations.stream()
			.map(this.elements::getTypeElement)
			.filter(Objects::nonNull)
			.collect(Collectors.toSet());
	}

	AnnotationMirror getReadOperationAnnotation(Element element) {
		return getAnnotation(element, this.readOperationAnnotation);
	}

	AnnotationMirror getNameAnnotation(Element element) {
		return getAnnotation(element, this.nameAnnotation);
	}

	boolean hasNullableAnnotation(Element element) {
		return getTypeUseAnnotation(element, NULLABLE_ANNOTATION) != null;
	}

	private boolean isElementDeprecated(Element element) {
		return hasAnnotation(element, "java.lang.Deprecated")
				|| hasAnnotation(element, this.deprecatedConfigurationPropertyAnnotation);
	}

	private Map<String, Object> resolveFieldValues(TypeElement element) {
		Map<String, Object> values = new LinkedHashMap<>();
		resolveFieldValuesFor(values, element);
		return values;
	}

	private void resolveFieldValuesFor(Map<String, Object> values, TypeElement element) {
		try {
			this.fieldValuesParser.getFieldValues(element).forEach((name, value) -> {
				if (!values.containsKey(name)) {
					values.put(name, value);
				}
			});
		}
		catch (Exception ex) {
			// continue
		}
		Element superType = this.typeUtils.asElement(element.getSuperclass());
		if (superType instanceof TypeElement && superType.asType().getKind() != TypeKind.NONE) {
			resolveFieldValuesFor(values, (TypeElement) superType);
		}
	}

}

Domain

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free