/* * 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.lang.syntax; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.tngtech.archunit.base.Function; import com.tngtech.archunit.base.Function.Functions; import com.tngtech.archunit.base.Optional; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.lang.ClassesTransformer; import com.tngtech.archunit.lang.EvaluationResult; import com.tngtech.archunit.lang.Priority; import static com.google.common.base.Preconditions.checkState; class ObjectsShouldInternal<T> implements ArchRule { private final Supplier<ArchRule> finishedRule = Suppliers.memoize(new FinishedRule()); final ConditionAggregator<T> conditionAggregator; final ClassesTransformer<T> classesTransformer; final Priority priority; final Function<ArchCondition<T>, ArchCondition<T>> prepareCondition; ObjectsShouldInternal(ClassesTransformer<T> classesTransformer, Priority priority, Function<ArchCondition<T>, ArchCondition<T>> prepareCondition) { this(classesTransformer, priority, new ConditionAggregator<>(AddMode.<T>and()), prepareCondition); } ObjectsShouldInternal(ClassesTransformer<T> classesTransformer, Priority priority, ArchCondition<T> condition, Function<ArchCondition<T>, ArchCondition<T>> prepareCondition) { this(classesTransformer, priority, new ConditionAggregator<>(condition, AddMode.<T>and()), prepareCondition); } ObjectsShouldInternal(ClassesTransformer<T> classesTransformer, Priority priority, ConditionAggregator<T> conditionAggregator, Function<ArchCondition<T>, ArchCondition<T>> prepareCondition) { this.conditionAggregator = conditionAggregator; this.classesTransformer = classesTransformer; this.priority = priority; this.prepareCondition = prepareCondition; } @Override public String getDescription() { return finishedRule.get().getDescription(); } @Override public EvaluationResult evaluate(JavaClasses classes) { return finishedRule.get().evaluate(classes); } @Override public void check(JavaClasses classes) { finishedRule.get().check(classes); } @Override public ArchRule because(String reason) { return ArchRule.Factory.withBecause(this, reason); } @Override public ArchRule as(String newDescription) { return finishedRule.get().as(newDescription); } @Override public String toString() { return finishedRule.get().getDescription(); } private class FinishedRule implements Supplier<ArchRule> { @Override public ArchRule get() { return ArchRule.Factory.create(classesTransformer, createCondition(), priority); } private ArchCondition<T> createCondition() { return prepareCondition.apply(conditionAggregator.getCondition()); } } static class ConditionAggregator<T> { private final Optional<ArchCondition<T>> condition; private final AddMode<T> addMode; ConditionAggregator(AddMode<T> addMode) { this(Optional.<ArchCondition<T>>absent(), addMode); } ConditionAggregator(ArchCondition<T> condition, AddMode<T> addMode) { this(Optional.of(condition), addMode); } private ConditionAggregator(Optional<ArchCondition<T>> condition, AddMode<T> addMode) { this.condition = condition; this.addMode = addMode; } ArchCondition<T> getCondition() { checkState(condition.isPresent(), "No condition was added to this rule, this is most likely a bug within the syntax"); return condition.get(); } ArchCondition<T> add(ArchCondition<? super T> other) { return addMode.apply(condition, other); } ConditionAggregator<T> thatORsWith(Function<ArchCondition<T>, ArchCondition<T>> prepareCondition) { return new ConditionAggregator<>(condition, AddMode.or(prepareCondition)); } ConditionAggregator<T> thatANDsWith(Function<ArchCondition<T>, ArchCondition<T>> prepareCondition) { return new ConditionAggregator<>(condition, AddMode.and(prepareCondition)); } } private abstract static class AddMode<T> { static <T> AddMode<T> and() { return and(Functions.<ArchCondition<T>>identity()); } static <T> AddMode<T> and(final Function<ArchCondition<T>, ArchCondition<T>> prepareCondition) { return new AddMode<T>() { @Override ArchCondition<T> apply(Optional<ArchCondition<T>> first, ArchCondition<? super T> other) { ArchCondition<T> second = prepareCondition.apply(other.<T>forSubType()); return first.isPresent() ? first.get().and(second) : second; } }; } static <T> AddMode<T> or(final Function<ArchCondition<T>, ArchCondition<T>> prepareCondition) { return new AddMode<T>() { @Override ArchCondition<T> apply(Optional<ArchCondition<T>> first, ArchCondition<? super T> other) { ArchCondition<T> second = prepareCondition.apply(other.<T>forSubType()); return first.isPresent() ? first.get().or(second) : second; } }; } abstract ArchCondition<T> apply(Optional<ArchCondition<T>> first, ArchCondition<? super T> other); } static <T> Function<ArchCondition<T>, ArchCondition<T>> prependDescription(final String prefix) { return new Function<ArchCondition<T>, ArchCondition<T>>() { @Override public ArchCondition<T> apply(ArchCondition<T> input) { return input.as(prefix + " " + input.getDescription()); } }; } }