Java tutorial
/* * Copyright 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 org.gradle.internal.component; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import org.apache.commons.lang.StringUtils; import org.gradle.api.attributes.Attribute; import org.gradle.api.attributes.AttributeContainer; import org.gradle.api.attributes.AttributeMatchingStrategy; import org.gradle.api.attributes.AttributesSchema; import org.gradle.api.attributes.CompatibilityCheckDetails; import org.gradle.api.internal.attributes.CompatibilityRuleChainInternal; import org.gradle.internal.Cast; import org.gradle.internal.component.model.ComponentResolveMetadata; import org.gradle.internal.component.model.ConfigurationMetadata; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; public class AmbiguousConfigurationSelectionException extends IllegalArgumentException { private static final Function<ConfigurationMetadata, String> CONFIG_NAME = new Function<ConfigurationMetadata, String>() { @Override public String apply(ConfigurationMetadata input) { return input.getName(); } }; private static final Joiner JOINER = Joiner.on(", "); static final Function<Attribute<?>, String> ATTRIBUTE_NAME = new Function<Attribute<?>, String>() { @Override public String apply(Attribute<?> input) { return input.getName(); } }; public AmbiguousConfigurationSelectionException(AttributeContainer fromConfigurationAttributes, AttributesSchema consumerSchema, List<ConfigurationMetadata> matches, ComponentResolveMetadata targetComponent) { super(generateMessage(fromConfigurationAttributes, consumerSchema, matches, targetComponent)); } private static String generateMessage(AttributeContainer fromConfigurationAttributes, AttributesSchema consumerSchema, List<ConfigurationMetadata> matches, ComponentResolveMetadata targetComponent) { Set<String> ambiguousConfigurations = Sets.newTreeSet(Lists.transform(matches, CONFIG_NAME)); Set<String> requestedAttributes = Sets .newTreeSet(Iterables.transform(fromConfigurationAttributes.keySet(), ATTRIBUTE_NAME)); StringBuilder sb = new StringBuilder( "Cannot choose between the following configurations on '" + targetComponent + "' : "); JOINER.appendTo(sb, ambiguousConfigurations); sb.append(". All of them match the consumer attributes:"); sb.append("\n"); int maxConfLength = maxLength(ambiguousConfigurations); // We're sorting the names of the configurations and later attributes // to make sure the output is consistently the same between invocations for (final String ambiguousConf : ambiguousConfigurations) { formatConfiguration(sb, fromConfigurationAttributes, consumerSchema, matches, requestedAttributes, maxConfLength, ambiguousConf); } return sb.toString(); } static void formatConfiguration(StringBuilder sb, AttributeContainer fromConfigurationAttributes, AttributesSchema consumerSchema, List<ConfigurationMetadata> matches, Set<String> requestedAttributes, int maxConfLength, final String conf) { Optional<ConfigurationMetadata> match = Iterables.tryFind(matches, new Predicate<ConfigurationMetadata>() { @Override public boolean apply(ConfigurationMetadata input) { return conf.equals(input.getName()); } }); if (match.isPresent()) { AttributeContainer producerAttributes = match.get().getAttributes(); Set<Attribute<?>> targetAttributes = producerAttributes.keySet(); Set<String> targetAttributeNames = Sets .newTreeSet(Iterables.transform(targetAttributes, ATTRIBUTE_NAME)); Set<Attribute<?>> allAttributes = Sets.union(fromConfigurationAttributes.keySet(), producerAttributes.keySet()); Set<String> commonAttributes = Sets.intersection(requestedAttributes, targetAttributeNames); Set<String> consumerOnlyAttributes = Sets.difference(requestedAttributes, targetAttributeNames); sb.append(" ").append("- Configuration '").append(StringUtils.rightPad(conf + "'", maxConfLength + 1)) .append(" :"); List<Attribute<?>> sortedAttributes = Ordering.usingToString().sortedCopy(allAttributes); List<String> values = new ArrayList<String>(sortedAttributes.size()); formatAttributes(sb, fromConfigurationAttributes, consumerSchema, producerAttributes, commonAttributes, consumerOnlyAttributes, sortedAttributes, values); } } private static void formatAttributes(StringBuilder sb, AttributeContainer fromConfigurationAttributes, AttributesSchema consumerSchema, AttributeContainer producerAttributes, Set<String> commonAttributes, Set<String> consumerOnlyAttributes, List<Attribute<?>> sortedAttributes, final List<String> values) { for (Attribute<?> attribute : sortedAttributes) { final String attributeName = attribute.getName(); if (commonAttributes.contains(attributeName)) { AttributeMatchingStrategy<?> strategy = consumerSchema.getMatchingStrategy(attribute); CompatibilityRuleChainInternal<Object> compatibilityRules = Cast .uncheckedCast(strategy.getCompatibilityRules()); final Object consumerValue = fromConfigurationAttributes.getAttribute(attribute); final Object producerValue = producerAttributes.getAttribute(attribute); compatibilityRules.execute(new CompatibilityCheckDetails<Object>() { @Override public Object getConsumerValue() { return consumerValue; } @Override public Object getProducerValue() { return producerValue; } @Override public void compatible() { values.add(" - " + "Required " + attributeName + " '" + consumerValue + "' and found compatible value '" + producerValue + "'."); } @Override public void incompatible() { values.add(" - " + "Required " + attributeName + " '" + consumerValue + "' and found incompatible value '" + producerValue + "'."); } }); } else if (consumerOnlyAttributes.contains(attributeName)) { values.add(" - " + "Required " + attributeName + " '" + fromConfigurationAttributes.getAttribute(attribute) + "' but no value provided."); } else { values.add(" - " + "Found " + attributeName + " '" + producerAttributes.getAttribute(attribute) + "' but wasn't required."); } } sb.append("\n"); sb.append(Joiner.on("\n").join(values)); sb.append("\n"); } static int maxLength(Collection<String> strings) { return Ordering.natural().max(Iterables.transform(strings, new Function<String, Integer>() { @Override public Integer apply(String input) { return input.length(); } })); } }