All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.api.internal.provider.AbstractCollectionProperty Maven / Gradle / Ivy

/*
 * Copyright 2017 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.api.internal.provider;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import org.gradle.api.Action;
import org.gradle.api.internal.provider.Collectors.ElementFromProvider;
import org.gradle.api.internal.provider.Collectors.ElementsFromArray;
import org.gradle.api.internal.provider.Collectors.ElementsFromCollection;
import org.gradle.api.internal.provider.Collectors.ElementsFromCollectionProvider;
import org.gradle.api.internal.provider.Collectors.SingleElement;
import org.gradle.api.provider.HasMultipleValues;
import org.gradle.api.provider.Provider;
import org.gradle.internal.Cast;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;

public abstract class AbstractCollectionProperty> extends AbstractProperty> implements CollectionPropertyInternal {
    private static final CollectionSupplier> NO_VALUE = new NoValueSupplier<>(Value.missing());
    private final Class collectionType;
    private final Class elementType;
    private final Supplier> collectionFactory;
    private final ValueCollector valueCollector;
    private CollectionSupplier defaultValue = emptySupplier();

    AbstractCollectionProperty(PropertyHost host, Class collectionType, Class elementType, Supplier> collectionFactory) {
        super(host);
        this.collectionType = collectionType;
        this.elementType = elementType;
        this.collectionFactory = collectionFactory;
        valueCollector = new ValidatingValueCollector<>(collectionType, elementType, ValueSanitizers.forType(elementType));
        init(defaultValue, noValueSupplier());
    }

    private CollectionSupplier emptySupplier() {
        return new EmptySupplier();
    }

    private CollectionSupplier noValueSupplier() {
        return Cast.uncheckedCast(NO_VALUE);
    }

    /**
     * Creates an empty immutable collection.
     */
    protected abstract C emptyCollection();

    @Override
    public void add(final T element) {
        Preconditions.checkNotNull(element, String.format("Cannot add a null element to a property of type %s.", collectionType.getSimpleName()));
        addCollector(new SingleElement<>(element));
    }

    @Override
    public void add(final Provider providerOfElement) {
        addCollector(new ElementFromProvider<>(Providers.internal(providerOfElement)));
    }

    @Override
    @SafeVarargs
    @SuppressWarnings("varargs")
    public final void addAll(T... elements) {
        addCollector(new ElementsFromArray<>(elements));
    }

    @Override
    public void addAll(Iterable elements) {
        addCollector(new ElementsFromCollection<>(elements));
    }

    @Override
    public void addAll(Provider> provider) {
        addCollector(new ElementsFromCollectionProvider<>(Providers.internal(provider)));
    }

    private void addCollector(Collector collector) {
        assertCanMutate();
        setSupplier(getExplicitValue(defaultValue).plus(collector));
    }

    @Nullable
    @Override
    public Class getType() {
        return Cast.uncheckedCast(collectionType);
    }

    @Override
    public Class getElementType() {
        return elementType;
    }

    /**
     * Sets the value of this property the given value.
     */
    public void fromState(ExecutionTimeValue value) {
        if (value.isMissing()) {
            setSupplier(noValueSupplier());
        } else if (value.isFixedValue()) {
            setSupplier(new FixedSupplier<>(value.getFixedValue()));
        } else {
            setSupplier(new CollectingSupplier(new ElementsFromCollectionProvider<>(value.getChangingValue())));
        }
    }

    @Override
    public void setFromAnyValue(Object object) {
        if (object instanceof Provider) {
            set(Cast.>uncheckedCast(object));
        } else {
            if (object != null && !(object instanceof Iterable)) {
                throw new IllegalArgumentException(String.format("Cannot set the value of a property of type %s using an instance of type %s.", collectionType.getName(), object.getClass().getName()));
            }
            set(Cast.>uncheckedCast(object));
        }
    }

    @Override
    public void set(@Nullable final Iterable elements) {
        if (elements == null) {
            discardValue();
            defaultValue = noValueSupplier();
        } else {
            setSupplier(new CollectingSupplier(new ElementsFromCollection<>(elements)));
        }
    }

    @Override
    public void set(final Provider> provider) {
        if (provider == null) {
            throw new IllegalArgumentException("Cannot set the value of a property using a null provider.");
        }
        ProviderInternal> p = Providers.internal(provider);
        if (p.getType() != null && !Iterable.class.isAssignableFrom(p.getType())) {
            throw new IllegalArgumentException(String.format("Cannot set the value of a property of type %s using a provider of type %s.", collectionType.getName(), p.getType().getName()));
        }
        if (p instanceof CollectionPropertyInternal) {
            CollectionPropertyInternal collectionProp = Cast.uncheckedCast(p);
            if (!elementType.isAssignableFrom(collectionProp.getElementType())) {
                throw new IllegalArgumentException(String.format("Cannot set the value of a property of type %s with element type %s using a provider with element type %s.", collectionType.getName(), elementType.getName(), collectionProp.getElementType().getName()));
            }
        }
        setSupplier(new CollectingSupplier(new ElementsFromCollectionProvider<>(p)));
    }

    @Override
    public HasMultipleValues value(@Nullable Iterable elements) {
        set(elements);
        return this;
    }

    @Override
    public HasMultipleValues value(Provider> provider) {
        set(provider);
        return this;
    }

    @Override
    public HasMultipleValues empty() {
        setSupplier(emptySupplier());
        return this;
    }

    @Override
    protected Value calculateValueFrom(CollectionSupplier value, ValueConsumer consumer) {
        return value.calculateValue(consumer);
    }

    @Override
    protected CollectionSupplier finalValue(CollectionSupplier value, ValueConsumer consumer) {
        Value result = value.calculateValue(consumer);
        if (!result.isMissing()) {
            return new FixedSupplier<>(result.get());
        } else if (result.getPathToOrigin().isEmpty()) {
            return noValueSupplier();
        } else {
            return new NoValueSupplier<>(result);
        }
    }

    @Override
    protected ExecutionTimeValue calculateOwnExecutionTimeValue(CollectionSupplier value) {
        return value.calculateExecutionTimeValue();
    }

    @Override
    public HasMultipleValues convention(@Nullable Iterable elements) {
        if (elements == null) {
            setConvention(noValueSupplier());
        } else {
            setConvention(new CollectingSupplier(new ElementsFromCollection<>(elements)));
        }
        return this;
    }

    @Override
    public HasMultipleValues convention(Provider> provider) {
        setConvention(new CollectingSupplier(new ElementsFromCollectionProvider<>(Providers.internal(provider))));
        return this;
    }

    @Override
    protected String describeContents() {
        return String.format("%s(%s, %s)", collectionType.getSimpleName().toLowerCase(), elementType, getSupplier().toString());
    }

    static class NoValueSupplier> implements CollectionSupplier {
        private final Value value;

        public NoValueSupplier(Value value) {
            assert value.isMissing();
            this.value = value.asType();
        }

        @Override
        public boolean calculatePresence(ValueConsumer consumer) {
            return false;
        }

        @Override
        public Value calculateValue(ValueConsumer consumer) {
            return value;
        }

        @Override
        public CollectionSupplier plus(Collector collector) {
            // No value + something = no value
            return this;
        }

        @Override
        public ExecutionTimeValue calculateExecutionTimeValue() {
            return ExecutionTimeValue.missing();
        }

        @Override
        public ValueProducer getProducer() {
            return ValueProducer.unknown();
        }
    }

    private class EmptySupplier implements CollectionSupplier {
        @Override
        public boolean calculatePresence(ValueConsumer consumer) {
            return true;
        }

        @Override
        public Value calculateValue(ValueConsumer consumer) {
            return Value.of(emptyCollection());
        }

        @Override
        public CollectionSupplier plus(Collector collector) {
            // empty + something = something
            return new CollectingSupplier(collector);
        }

        @Override
        public ExecutionTimeValue calculateExecutionTimeValue() {
            return ExecutionTimeValue.fixedValue(emptyCollection());
        }

        @Override
        public ValueProducer getProducer() {
            return ValueProducer.noProducer();
        }
    }

    private static class FixedSupplier> implements CollectionSupplier {
        private final C value;

        public FixedSupplier(C value) {
            this.value = value;
        }

        @Override
        public boolean calculatePresence(ValueConsumer consumer) {
            return true;
        }

        @Override
        public Value calculateValue(ValueConsumer consumer) {
            return Value.of(value);
        }

        @Override
        public CollectionSupplier plus(Collector collector) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ExecutionTimeValue calculateExecutionTimeValue() {
            return ExecutionTimeValue.fixedValue(value);
        }

        @Override
        public ValueProducer getProducer() {
            return ValueProducer.unknown();
        }
    }

    private class CollectingSupplier implements CollectionSupplier {
        private final Collector value;

        public CollectingSupplier(Collector value) {
            this.value = value;
        }

        @Override
        public boolean calculatePresence(ValueConsumer consumer) {
            return value.calculatePresence(consumer);
        }

        @Override
        public Value calculateValue(ValueConsumer consumer) {
            // TODO - don't make a copy when the collector already produces an immutable collection
            ImmutableCollection.Builder builder = collectionFactory.get();
            Value result = value.collectEntries(consumer, valueCollector, builder);
            if (result.isMissing()) {
                return result.asType();
            }
            return Value.of(Cast.uncheckedCast(builder.build()));
        }

        @Override
        public CollectionSupplier plus(Collector collector) {
            return new CollectingSupplier(new PlusCollector<>(value, collector));
        }

        @Override
        public ExecutionTimeValue calculateExecutionTimeValue() {
            List>> values = new ArrayList<>();
            value.calculateExecutionTimeValue(values::add);
            boolean fixed = true;
            boolean changingContent = false;
            for (ExecutionTimeValue> value : values) {
                if (value.isMissing()) {
                    return ExecutionTimeValue.missing();
                }
                if (value.isChangingValue()) {
                    fixed = false;
                }
                changingContent |= value.hasChangingContent();
            }

            if (fixed) {
                ImmutableCollection.Builder builder = collectionFactory.get();
                for (ExecutionTimeValue> value : values) {
                    builder.addAll(value.getFixedValue());
                }
                ExecutionTimeValue mergedValue = ExecutionTimeValue.fixedValue(Cast.uncheckedNonnullCast(builder.build()));
                if (changingContent) {
                    return mergedValue.withChangingContent();
                } else {
                    return mergedValue;
                }
            }

            // At least one of the values is a changing value
            List>> providers = new ArrayList<>(values.size());
            for (ExecutionTimeValue> value : values) {
                providers.add(value.toProvider());
            }
            // TODO - CollectionSupplier could be replaced with ProviderInternal, so this type and the collection provider can be merged
            return ExecutionTimeValue.changingValue(new CollectingProvider<>(AbstractCollectionProperty.this.getType(), providers, collectionFactory));
        }

        @Override
        public ValueProducer getProducer() {
            return value.getProducer();
        }
    }

    private static class CollectingProvider> extends AbstractMinimalProvider {
        private final Class type;
        private final List>> providers;
        private final Supplier> collectionFactory;

        public CollectingProvider(Class type, List>> providers, Supplier> collectionFactory) {
            this.type = type;
            this.providers = providers;
            this.collectionFactory = collectionFactory;
        }

        @Nullable
        @Override
        public Class getType() {
            return type;
        }

        @Override
        public ExecutionTimeValue calculateExecutionTimeValue() {
            return ExecutionTimeValue.changingValue(this);
        }

        @Override
        protected Value calculateOwnValue(ValueConsumer consumer) {
            ImmutableCollection.Builder builder = collectionFactory.get();
            for (ProviderInternal> provider : providers) {
                Value> value = provider.calculateValue(consumer);
                if (value.isMissing()) {
                    return Value.missing();
                }
                builder.addAll(value.get());
            }
            return Value.of(Cast.uncheckedNonnullCast(builder.build()));
        }
    }

    private static class PlusCollector implements Collector {
        private final Collector left;
        private final Collector right;

        public PlusCollector(Collector left, Collector right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public boolean calculatePresence(ValueConsumer consumer) {
            return left.calculatePresence(consumer) && right.calculatePresence(consumer);
        }

        @Override
        public int size() {
            return left.size() + right.size();
        }

        @Override
        public Value collectEntries(ValueConsumer consumer, ValueCollector collector, ImmutableCollection.Builder dest) {
            Value value = left.collectEntries(consumer, collector, dest);
            if (value.isMissing()) {
                return value;
            }
            return right.collectEntries(consumer, collector, dest);
        }

        @Override
        public void calculateExecutionTimeValue(Action>> visitor) {
            left.calculateExecutionTimeValue(visitor);
            right.calculateExecutionTimeValue(visitor);
        }

        @Override
        public ValueProducer getProducer() {
            return left.getProducer().plus(right.getProducer());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy