/* * Copyright 2017 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.library.dependencies; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import com.google.common.base.Joiner; import com.google.common.collect.ForwardingSet; import com.google.common.collect.ImmutableSet; import com.tngtech.archunit.PublicAPI; import com.tngtech.archunit.core.domain.Dependency; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.domain.properties.CanOverrideDescription; import com.tngtech.archunit.core.domain.properties.HasDescription; import static com.google.common.base.Preconditions.checkArgument; import static com.tngtech.archunit.PublicAPI.Usage.ACCESS; public final class Slice extends ForwardingSet<JavaClass> implements HasDescription, CanOverrideDescription<Slice> { private final List<String> matchingGroups; private Description description; private final Set<JavaClass> classes; private Slice(List<String> matchingGroups, Set<JavaClass> classes) { this.matchingGroups = matchingGroups; this.description = new Description("Slice " + Joiner.on(" - ").join(ascendingCaptures(matchingGroups))); this.classes = ImmutableSet.copyOf(classes); } private List<String> ascendingCaptures(List<String> matchingGroups) { List<String> result = new ArrayList<>(); for (int i = 1; i <= matchingGroups.size(); i++) { result.add("$" + i); } return result; } @Override protected Set<JavaClass> delegate() { return classes; } @Override public String getDescription() { return description.format(matchingGroups); } /** * The pattern can be a description with references to the matching groups by '$' and position. * E.g. slices are created by 'some.svc.(*).sub.(*)', and the pattern is "the module $2 of service $1", * and we match 'some.svc.foo.module.bar', then the resulting description will be * "the module bar of service foo". * * @param pattern The description pattern with numbered references of the form $i * @return Same slice with different description */ @Override public Slice as(String pattern) { description = new Description(pattern); return this; } @PublicAPI(usage = ACCESS) public Set<Dependency> getDependencies() { Set<Dependency> result = new HashSet<>(); for (JavaClass javaClass : this) { for (Dependency dependency : javaClass.getDirectDependencies()) { if (!contains(dependency.getTargetClass())) { result.add(dependency); } } } return result; } @Override public String toString() { return getDescription(); } /** * Returns a matching part of this slice. E.g. if the slice was created by matching '..(*).controller.(*)..', * against 'some.other.controller.here.more', then name part '1' would be 'other' and name part '2' would * be 'here'. * * @param index The index of the matched group * @return The part of the matched package name. */ @PublicAPI(usage = ACCESS) public String getNamePart(int index) { checkArgument(index > 0 && index <= matchingGroups.size(), "Found no name part with index %d", index); return matchingGroups.get(index - 1); } private static class Description { private final String pattern; private Description(String pattern) { this.pattern = pattern; } String format(List<String> matchingGroups) { String result = pattern; for (int i = 1; i <= matchingGroups.size(); i++) { result = result.replace("$" + i, matchingGroups.get(i - 1)); } return result; } } static class Builder { private final List<String> matchingGroups; private Set<JavaClass> classes = new HashSet<>(); private Builder(List<String> matchingGroups) { this.matchingGroups = matchingGroups; } static Builder from(List<String> matchingGroups) { return new Builder(matchingGroups); } Builder addClass(JavaClass clazz) { classes.add(clazz); return this; } Slice build() { return new Slice(matchingGroups, classes); } } }