com.tngtech.archunit.core.domain.JavaClassMembers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of archunit Show documentation
Show all versions of archunit Show documentation
A Java architecture test library, to specify and assert architecture rules in plain Java - Module 'archunit'
The newest version!
/*
* Copyright 2014-2024 TNG Technology Consulting GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tngtech.archunit.core.domain;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Supplier;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.tngtech.archunit.base.Suppliers;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.tngtech.archunit.base.Optionals.asSet;
import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME;
import static com.tngtech.archunit.core.domain.JavaModifier.ENUM;
import static com.tngtech.archunit.core.domain.JavaModifier.SYNTHETIC;
import static com.tngtech.archunit.core.domain.properties.HasName.Utils.namesOf;
import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toSet;
class JavaClassMembers {
private final JavaClass owner;
private final Set fields;
private final Set codeUnits;
private final Set methods;
private final Set members;
private final Set constructors;
private final Optional staticInitializer;
private final Supplier> allMethods;
private final Supplier> allConstructors;
private final Supplier> allFields;
private final Supplier> allMembers = Suppliers.memoize(() -> ImmutableSet.builder()
.addAll(getAllFields())
.addAll(getAllMethods())
.addAll(getAllConstructors())
.build());
JavaClassMembers(JavaClass owner, Set fields, Set methods, Set constructors, Optional staticInitializer) {
this.owner = owner;
this.fields = fields;
this.methods = methods;
this.constructors = constructors;
this.staticInitializer = staticInitializer;
this.codeUnits = ImmutableSet.builder()
.addAll(methods).addAll(constructors).addAll(asSet(staticInitializer))
.build();
this.members = ImmutableSet.builder()
.addAll(fields)
.addAll(methods)
.addAll(constructors)
.build();
allFields = Suppliers.memoize(() -> {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaClass javaClass : concat(owner.getClassHierarchy(), owner.getAllRawInterfaces())) {
result.addAll(javaClass.getFields());
}
return result.build();
});
allMethods = Suppliers.memoize(() -> {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaClass javaClass : concat(owner.getClassHierarchy(), owner.getAllRawInterfaces())) {
result.addAll(javaClass.getMethods());
}
return result.build();
});
allConstructors = Suppliers.memoize(() -> {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaClass javaClass : owner.getClassHierarchy()) {
result.addAll(javaClass.getConstructors());
}
return result.build();
});
}
Set get() {
return members;
}
Set getAll() {
return allMembers.get();
}
Set getFields() {
return fields;
}
Set getAllFields() {
return allFields.get();
}
public JavaField getField(String name) {
Optional field = tryGetField(name);
if (!field.isPresent()) {
throw new IllegalArgumentException("No field with name '" + name + " in class " + owner.getName());
}
return field.get();
}
Optional tryGetField(String name) {
for (JavaField field : fields) {
if (name.equals(field.getName())) {
return Optional.of(field);
}
}
return Optional.empty();
}
Set getCodeUnits() {
return codeUnits;
}
JavaCodeUnit getCodeUnitWithParameterTypeNames(String name, List parameters) {
return findMatchingCodeUnit(codeUnits, name, parameters);
}
Optional tryGetCodeUnitWithParameterTypeNames(String name, List parameters) {
return tryFindMatchingCodeUnit(codeUnits, name, parameters);
}
JavaMethod getMethod(String name, List parameterTypeNames) {
return findMatchingCodeUnit(methods, name, ImmutableList.copyOf(parameterTypeNames));
}
Optional tryGetMethod(String name, List parameterTypeNames) {
return tryFindMatchingCodeUnit(methods, name, parameterTypeNames);
}
Set getMethods() {
return methods;
}
Set getAllMethods() {
return allMethods.get();
}
JavaConstructor getConstructor(List parameterTypeNames) {
return findMatchingCodeUnit(constructors, CONSTRUCTOR_NAME, parameterTypeNames);
}
Optional tryGetConstructor(List parameterTypeNames) {
return tryFindMatchingCodeUnit(constructors, CONSTRUCTOR_NAME, parameterTypeNames);
}
Set getConstructors() {
return constructors;
}
Set getAllConstructors() {
return allConstructors.get();
}
Optional getStaticInitializer() {
return staticInitializer;
}
Set getEnumConstants() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaField field : fields) {
if (field.getModifiers().contains(ENUM)) {
result.add(new JavaEnumConstant(owner, field.getName()));
}
}
return result.build();
}
Set getInstanceofChecks() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : codeUnits) {
result.addAll(codeUnit.getInstanceofChecks());
}
return result.build();
}
Set getReferencedClassObjects() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : codeUnits) {
result.addAll(codeUnit.getReferencedClassObjects());
}
return result.build();
}
Set getFieldAccessesFromSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : codeUnits) {
result.addAll(codeUnit.getFieldAccesses());
}
return result.build();
}
Set getMethodCallsFromSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : codeUnits) {
result.addAll(codeUnit.getMethodCallsFromSelf());
}
return result.build();
}
Set getConstructorCallsFromSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : codeUnits) {
result.addAll(codeUnit.getConstructorCallsFromSelf());
}
return result.build();
}
Set getMethodReferencesFromSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : codeUnits) {
result.addAll(codeUnit.getMethodReferencesFromSelf());
}
return result.build();
}
Set getConstructorReferencesFromSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaCodeUnit codeUnit : codeUnits) {
result.addAll(codeUnit.getConstructorReferencesFromSelf());
}
return result.build();
}
Set getFieldAccessesToSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaField field : fields) {
result.addAll(field.getAccessesToSelf());
}
return result.build();
}
Set getMethodCallsToSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaMethod method : methods) {
result.addAll(method.getCallsOfSelf());
}
return result.build();
}
Set getMethodReferencesToSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaMethod method : methods) {
result.addAll(method.getReferencesToSelf());
}
return result.build();
}
Set getConstructorCallsToSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaConstructor constructor : constructors) {
result.addAll(constructor.getCallsOfSelf());
}
return result.build();
}
Set getConstructorReferencesToSelf() {
ImmutableSet.Builder result = ImmutableSet.builder();
for (JavaConstructor constructor : constructors) {
result.addAll(constructor.getReferencesToSelf());
}
return result.build();
}
private T findMatchingCodeUnit(Set codeUnits, String name, List parameters) {
Optional codeUnit = tryFindMatchingCodeUnit(codeUnits, name, parameters);
if (!codeUnit.isPresent()) {
throw new IllegalArgumentException(
String.format("No code unit with name '%s' and parameters %s in codeUnits %s of class %s",
name, parameters, codeUnits, owner.getName()));
}
return codeUnit.get();
}
private Optional tryFindMatchingCodeUnit(Set codeUnits, String name, List parameters) {
Set matching = findCodeUnitsWithMatchingNameAndParameters(codeUnits, name, parameters);
if (matching.isEmpty()) {
return Optional.empty();
} else if (matching.size() == 1) {
return Optional.of(getOnlyElement(matching));
} else {
// In this case we have some synthetic methods like bridge methods making name and parameters alone ambiguous
// We want to return the non-synthetic method first because that is usually the relevant one for users
SortedSet sortedByPriority = new TreeSet<>(SORTED_BY_SYNTHETIC_LAST_THEN_FULL_NAME);
sortedByPriority.addAll(matching);
return Optional.of(sortedByPriority.first());
}
}
private Set findCodeUnitsWithMatchingNameAndParameters(Set codeUnits, String name, List parameters) {
return codeUnits.stream()
.filter(codeUnit -> name.equals(codeUnit.getName()))
.filter(codeUnit -> parameters.equals(namesOf(codeUnit.getRawParameterTypes())))
.collect(toSet());
}
void completeAnnotations(ImportContext context) {
for (JavaMember member : members) {
member.completeAnnotations(context);
}
}
void completeFrom(ImportContext context) {
for (JavaCodeUnit codeUnit : codeUnits) {
codeUnit.completeFrom(context);
}
}
void setReverseDependencies(ReverseDependencies reverseDependencies) {
for (JavaMember member : members) {
member.setReverseDependencies(reverseDependencies);
}
}
private static final Comparator SORTED_BY_SYNTHETIC_LAST_THEN_FULL_NAME =
(codeUnit1, codeUnit2) -> ComparisonChain.start()
.compareTrueFirst(!codeUnit1.getModifiers().contains(SYNTHETIC), !codeUnit2.getModifiers().contains(SYNTHETIC))
.compare(codeUnit1.getFullName(), codeUnit2.getFullName())
.result();
static JavaClassMembers empty(JavaClass owner) {
return new JavaClassMembers(
owner,
emptySet(),
emptySet(),
emptySet(),
Optional.empty());
}
static JavaClassMembers create(JavaClass owner, ImportContext context) {
return new JavaClassMembers(
owner,
context.createFields(owner),
context.createMethods(owner),
context.createConstructors(owner),
context.createStaticInitializer(owner));
}
}