Home / Class/ TypeElementMembers Class — spring-boot Architecture

TypeElementMembers Class — spring-boot Architecture

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

Entity Profile

Source Code

configuration-metadata/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeElementMembers.java lines 45–241

class TypeElementMembers {

	private static final String OBJECT_CLASS_NAME = Object.class.getName();

	private static final String RECORD_CLASS_NAME = Record.class.getName();

	private final MetadataGenerationEnvironment env;

	private final TypeElement targetType;

	private final boolean isRecord;

	private final Map<String, VariableElement> fields = new LinkedHashMap<>();

	private final Map<String, RecordComponentElement> recordComponents = new LinkedHashMap<>();

	private final Map<String, List<ExecutableElement>> publicGetters = new LinkedHashMap<>();

	private final Map<String, List<ExecutableElement>> publicSetters = new LinkedHashMap<>();

	TypeElementMembers(MetadataGenerationEnvironment env, TypeElement targetType) {
		this.env = env;
		this.targetType = targetType;
		this.isRecord = RECORD_CLASS_NAME.equals(targetType.getSuperclass().toString());
		process(targetType);
	}

	private void process(TypeElement element) {
		for (VariableElement field : ElementFilter.fieldsIn(element.getEnclosedElements())) {
			processField(field);
		}
		for (RecordComponentElement recordComponent : ElementFilter.recordComponentsIn(element.getEnclosedElements())) {
			processRecordComponent(recordComponent);
		}
		for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
			processMethod(method);
		}
		Element superType = this.env.getTypeUtils().asElement(element.getSuperclass());
		if (superType instanceof TypeElement && !OBJECT_CLASS_NAME.equals(superType.toString())
				&& !RECORD_CLASS_NAME.equals(superType.toString())) {
			process((TypeElement) superType);
		}
	}

	private void processMethod(ExecutableElement method) {
		if (isPublic(method)) {
			String name = method.getSimpleName().toString();
			if (isGetter(method)) {
				String propertyName = getAccessorName(name);
				List<ExecutableElement> matchingGetters = this.publicGetters.computeIfAbsent(propertyName,
						(k) -> new ArrayList<>());
				TypeMirror returnType = method.getReturnType();
				if (getMatchingGetter(matchingGetters, returnType) == null) {
					matchingGetters.add(method);
				}
			}
			else if (isSetter(method)) {
				String propertyName = getAccessorName(name);
				List<ExecutableElement> matchingSetters = this.publicSetters.computeIfAbsent(propertyName,
						(k) -> new ArrayList<>());
				TypeMirror paramType = method.getParameters().get(0).asType();
				if (getMatchingSetter(matchingSetters, paramType) == null) {
					matchingSetters.add(method);
				}
			}
		}
	}

	private boolean isPublic(ExecutableElement method) {
		Set<Modifier> modifiers = method.getModifiers();
		return modifiers.contains(Modifier.PUBLIC) && !modifiers.contains(Modifier.ABSTRACT)
				&& !modifiers.contains(Modifier.STATIC);
	}

	ExecutableElement getMatchingGetter(List<ExecutableElement> candidates, TypeMirror type) {
		return getMatchingAccessor(candidates, type, ExecutableElement::getReturnType);
	}

	private ExecutableElement getMatchingSetter(List<ExecutableElement> candidates, TypeMirror type) {
		return getMatchingAccessor(candidates, type, (candidate) -> candidate.getParameters().get(0).asType());
	}

	private ExecutableElement getMatchingAccessor(List<ExecutableElement> candidates, TypeMirror type,
			Function<ExecutableElement, TypeMirror> typeExtractor) {
		for (ExecutableElement candidate : candidates) {
			TypeMirror candidateType = typeExtractor.apply(candidate);
			if (this.env.getTypeUtils().isSameType(candidateType, type)) {
				return candidate;
			}
		}
		return null;
	}

	private boolean isGetter(ExecutableElement method) {
		boolean hasParameters = !method.getParameters().isEmpty();
		boolean returnsVoid = TypeKind.VOID == method.getReturnType().getKind();
		if (hasParameters || returnsVoid) {
			return false;
		}
		String name = method.getSimpleName().toString();
		if (this.isRecord && this.fields.containsKey(name)) {
			return true;
		}
		return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
	}

	private boolean isSetter(ExecutableElement method) {
		if (this.isRecord) {
			return false;
		}
		final String name = method.getSimpleName().toString();
		return (name.startsWith("set") && name.length() > 3 && method.getParameters().size() == 1
				&& isSetterReturnType(method));
	}

	private boolean isSetterReturnType(ExecutableElement method) {
		TypeMirror returnType = method.getReturnType();
		if (TypeKind.VOID == returnType.getKind()) {
			return true;
		}
		if (TypeKind.DECLARED == returnType.getKind()
				&& this.env.getTypeUtils().isSameType(method.getEnclosingElement().asType(), returnType)) {
			return true;
		}
		if (TypeKind.TYPEVAR == returnType.getKind()) {
			String resolvedType = this.env.getTypeUtils().getType(this.targetType, returnType);
			return (resolvedType != null
					&& resolvedType.equals(this.env.getTypeUtils().getQualifiedName(this.targetType)));
		}
		return false;
	}

	private String getAccessorName(String methodName) {
		if (this.isRecord && this.fields.containsKey(methodName)) {
			return methodName;
		}
		if (methodName.startsWith("is")) {
			return lowerCaseFirstCharacter(methodName.substring(2));
		}
		if (methodName.startsWith("get") || methodName.startsWith("set")) {
			return lowerCaseFirstCharacter(methodName.substring(3));
		}
		throw new IllegalStateException("methodName must start with 'is', 'get' or 'set', was '" + methodName + "'");
	}

	private String lowerCaseFirstCharacter(String string) {
		return Character.toLowerCase(string.charAt(0)) + string.substring(1);
	}

	private void processField(VariableElement field) {
		String name = field.getSimpleName().toString();
		this.fields.putIfAbsent(name, field);
	}

	private void processRecordComponent(RecordComponentElement recordComponent) {
		String name = recordComponent.getSimpleName().toString();
		this.recordComponents.putIfAbsent(name, recordComponent);
	}

	Map<String, VariableElement> getFields() {
		return Collections.unmodifiableMap(this.fields);
	}

	Map<String, RecordComponentElement> getRecordComponents() {
		return Collections.unmodifiableMap(this.recordComponents);
	}

	Map<String, List<ExecutableElement>> getPublicGetters() {
		return Collections.unmodifiableMap(this.publicGetters);
	}

	ExecutableElement getPublicGetter(String name, TypeMirror type) {
		List<ExecutableElement> candidates = this.publicGetters.get(name);
		return getPublicAccessor(candidates, type, (specificType) -> getMatchingGetter(candidates, specificType));
	}

	ExecutableElement getPublicSetter(String name, TypeMirror type) {
		List<ExecutableElement> candidates = this.publicSetters.get(name);
		return getPublicAccessor(candidates, type, (specificType) -> getMatchingSetter(candidates, specificType));
	}

	private ExecutableElement getPublicAccessor(List<ExecutableElement> candidates, TypeMirror type,
			Function<TypeMirror, ExecutableElement> matchingAccessorExtractor) {
		if (candidates != null) {
			ExecutableElement matching = matchingAccessorExtractor.apply(type);
			if (matching != null) {
				return matching;
			}
			TypeMirror alternative = this.env.getTypeUtils().getWrapperOrPrimitiveFor(type);
			if (alternative != null) {
				return matchingAccessorExtractor.apply(alternative);
			}
		}
		return null;
	}

}

Analyze Your Own Codebase

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

Try Supermodel Free