lodsve.core.condition.AbstractNestedCondition Maven / Gradle / Ivy
/*
* Copyright 2012-2016 the original author or authors.
*
* 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 lodsve.core.condition;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Abstract base class for nested conditions.
*
* @author Phillip Webb
*/
abstract class AbstractNestedCondition extends SpringBootCondition
implements ConfigurationCondition {
private final ConfigurationPhase configurationPhase;
AbstractNestedCondition(ConfigurationPhase configurationPhase) {
Assert.notNull(configurationPhase, "ConfigurationPhase must not be null");
this.configurationPhase = configurationPhase;
}
@Override
public ConfigurationPhase getConfigurationPhase() {
return this.configurationPhase;
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String className = getClass().getName();
MemberConditions memberConditions = new MemberConditions(context, className);
MemberMatchOutcomes memberOutcomes = new MemberMatchOutcomes(memberConditions);
return getFinalMatchOutcome(memberOutcomes);
}
protected abstract ConditionOutcome getFinalMatchOutcome(
MemberMatchOutcomes memberOutcomes);
protected static class MemberMatchOutcomes {
private final List all;
private final List matches;
private final List nonMatches;
public MemberMatchOutcomes(MemberConditions memberConditions) {
this.all = Collections.unmodifiableList(memberConditions.getMatchOutcomes());
List matches = new ArrayList();
List nonMatches = new ArrayList();
for (ConditionOutcome outcome : this.all) {
(outcome.isMatch() ? matches : nonMatches).add(outcome);
}
this.matches = Collections.unmodifiableList(matches);
this.nonMatches = Collections.unmodifiableList(nonMatches);
}
public List getAll() {
return this.all;
}
public List getMatches() {
return this.matches;
}
public List getNonMatches() {
return this.nonMatches;
}
}
private static class MemberConditions {
private final ConditionContext context;
private final MetadataReaderFactory readerFactory;
private final Map> memberConditions;
MemberConditions(ConditionContext context, String className) {
this.context = context;
this.readerFactory = new SimpleMetadataReaderFactory(
context.getResourceLoader());
String[] members = getMetadata(className).getMemberClassNames();
this.memberConditions = getMemberConditions(members);
}
private Map> getMemberConditions(
String[] members) {
MultiValueMap memberConditions = new LinkedMultiValueMap();
for (String member : members) {
AnnotationMetadata metadata = getMetadata(member);
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass);
memberConditions.add(metadata, condition);
}
}
}
return Collections.unmodifiableMap(memberConditions);
}
private AnnotationMetadata getMetadata(String className) {
try {
return this.readerFactory.getMetadataReader(className)
.getAnnotationMetadata();
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
@SuppressWarnings("unchecked")
private List getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap attributes = metadata
.getAllAnnotationAttributes(Conditional.class.getName(), true);
Object values = (attributes != null ? attributes.get("value") : null);
return (List) (values != null ? values : Collections.emptyList());
}
private Condition getCondition(String conditionClassName) {
Class> conditionClass = ClassUtils.resolveClassName(conditionClassName,
this.context.getClassLoader());
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
public List getMatchOutcomes() {
List outcomes = new ArrayList();
for (Map.Entry> entry : this.memberConditions
.entrySet()) {
AnnotationMetadata metadata = entry.getKey();
List conditions = entry.getValue();
outcomes.add(new MemberOutcomes(this.context, metadata, conditions)
.getUltimateOutcome());
}
return Collections.unmodifiableList(outcomes);
}
}
private static class MemberOutcomes {
private final ConditionContext context;
private final AnnotationMetadata metadata;
private final List outcomes;
MemberOutcomes(ConditionContext context, AnnotationMetadata metadata,
List conditions) {
this.context = context;
this.metadata = metadata;
this.outcomes = new ArrayList(conditions.size());
for (Condition condition : conditions) {
this.outcomes.add(getConditionOutcome(metadata, condition));
}
}
private ConditionOutcome getConditionOutcome(AnnotationMetadata metadata,
Condition condition) {
if (condition instanceof SpringBootCondition) {
return ((SpringBootCondition) condition).getMatchOutcome(this.context,
metadata);
}
return new ConditionOutcome(condition.matches(this.context, metadata),
ConditionMessage.empty());
}
public ConditionOutcome getUltimateOutcome() {
ConditionMessage.Builder message = ConditionMessage
.forCondition("NestedCondition on "
+ ClassUtils.getShortName(this.metadata.getClassName()));
if (this.outcomes.size() == 1) {
ConditionOutcome outcome = this.outcomes.get(0);
return new ConditionOutcome(outcome.isMatch(),
message.because(outcome.getMessage()));
}
List match = new ArrayList();
List nonMatch = new ArrayList();
for (ConditionOutcome outcome : this.outcomes) {
(outcome.isMatch() ? match : nonMatch).add(outcome);
}
if (nonMatch.isEmpty()) {
return ConditionOutcome
.match(message.found("matching nested conditions").items(match));
}
return ConditionOutcome.noMatch(
message.found("non-matching nested conditions").items(nonMatch));
}
}
}