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

org.gradle.api.internal.DefaultDomainObjectCollection Maven / Gradle / Ivy

There is a newer version: 8.6
Show newest version
/*
 * Copyright 2009 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;

import com.google.common.collect.Lists;
import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.DomainObjectCollection;
import org.gradle.api.internal.collections.CollectionEventRegister;
import org.gradle.api.internal.collections.CollectionFilter;
import org.gradle.api.internal.collections.DefaultCollectionEventRegister;
import org.gradle.api.internal.collections.ElementSource;
import org.gradle.api.internal.collections.FilteredCollection;
import org.gradle.api.internal.provider.CollectionProviderInternal;
import org.gradle.api.internal.provider.ProviderInternal;
import org.gradle.api.internal.provider.Providers;
import org.gradle.api.provider.Provider;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
import org.gradle.internal.Cast;
import org.gradle.util.ConfigureUtil;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class DefaultDomainObjectCollection extends AbstractCollection implements DomainObjectCollection, WithEstimatedSize, WithMutationGuard {

    private final Class type;
    private final CollectionEventRegister eventRegister;
    private final ElementSource store;

    protected DefaultDomainObjectCollection(Class type, ElementSource store, CollectionCallbackActionDecorator callbackActionDecorator) {
        this(type, store, new DefaultCollectionEventRegister(type, callbackActionDecorator));
    }

    protected DefaultDomainObjectCollection(Class type, ElementSource store, final CollectionEventRegister eventRegister) {
        this.type = type;
        this.store = store;
        this.eventRegister = eventRegister;
        this.store.onRealize(new Action() {
            @Override
            public void execute(T value) {
                doAddRealized(value, eventRegister.getAddActions());
            }
        });
    }

    protected DefaultDomainObjectCollection(DefaultDomainObjectCollection collection, CollectionFilter filter) {
        this(filter.getType(), collection.filteredStore(filter), collection.filteredEvents(filter));
    }

    protected void realized(ProviderInternal provider) {
        getStore().realizeExternal(provider);
    }

    public Class getType() {
        return type;
    }

    protected ElementSource getStore() {
        return store;
    }

    protected CollectionEventRegister getEventRegister() {
        return eventRegister;
    }

    protected CollectionFilter createFilter(Spec filter) {
        return createFilter(getType(), filter);
    }

    protected  CollectionFilter createFilter(Class type) {
        return new CollectionFilter(type);
    }

    protected  CollectionFilter createFilter(Class type, Spec spec) {
        return new CollectionFilter(type, spec);
    }

    protected  DefaultDomainObjectCollection filtered(CollectionFilter filter) {
        return new DefaultDomainObjectCollection(this, filter);
    }

    protected  ElementSource filteredStore(final CollectionFilter filter) {
        return filteredStore(filter, store);
    }

    protected  ElementSource filteredStore(CollectionFilter filter, ElementSource elementSource) {
        return new FilteredCollection(elementSource, filter);
    }

    protected  CollectionEventRegister filteredEvents(CollectionFilter filter) {
        return eventRegister.filtered(filter);
    }

    public DomainObjectCollection matching(final Spec spec) {
        return filtered(createFilter(spec));
    }

    public DomainObjectCollection matching(Closure spec) {
        return matching(Specs.convertClosureToSpec(spec));
    }

    public  DomainObjectCollection withType(final Class type) {
        return filtered(createFilter(type));
    }

    public Iterator iterator() {
        return new IteratorImpl(store.iterator());
    }

    Iterator iteratorNoFlush() {
        if (store.constantTimeIsEmpty()) {
            return Collections.emptyIterator();
        }

        return new IteratorImpl(store.iteratorNoFlush());
    }

    public void all(Action action) {
        assertMutable("all(Action)");
        Action decoratedAction = addEagerAction(action);

        if (store.constantTimeIsEmpty()) {
            return;
        }

        // copy in case any actions mutate the store
        // linked list because the underlying store may preserve order
        // We make best effort not to create an intermediate collection if this container
        // is empty.
        Collection copied = null;
        for (T t : this) {
            if (copied == null) {
                copied = Lists.newArrayListWithExpectedSize(estimatedSize());
            }
            copied.add(t);
        }
        if (copied != null) {
            for (T t : copied) {
                decoratedAction.execute(t);
            }
        }
    }

    @Override
    public void configureEach(Action action) {
        assertMutable("configureEach(Action)");
        Action wrappedAction = withMutationDisabled(decorate(action));
        Action registerLazyAddActionDecorated = eventRegister.registerLazyAddAction(wrappedAction);

        // copy in case any actions mutate the store
        Collection copied = null;
        Iterator iterator = iteratorNoFlush();
        while (iterator.hasNext()) {
            // only create an intermediate collection if there's something to copy
            if (copied == null) {
                copied = Lists.newArrayListWithExpectedSize(estimatedSize());
            }
            copied.add(iterator.next());
        }

        if (copied != null) {
            for (T next : copied) {
                registerLazyAddActionDecorated.execute(next);
            }
        }
    }

    protected  Action withMutationDisabled(Action action) {
        return getMutationGuard().withMutationDisabled(action);
    }

    public void all(Closure action) {
        all(toAction(action));
    }

    public  DomainObjectCollection withType(Class type, Action configureAction) {
        assertMutable("withType(Class, Action)");
        DomainObjectCollection result = withType(type);
        result.all(configureAction);
        return result;
    }

    public  DomainObjectCollection withType(Class type, Closure configureClosure) {
        return withType(type, toAction(configureClosure));
    }

    public Action whenObjectAdded(Action action) {
        assertMutable("whenObjectAdded(Action)");
        return addEagerAction(action);
    }

    public void whenObjectAdded(Closure action) {
        whenObjectAdded(toAction(action));
    }

    private Action addEagerAction(Action action) {
        store.realizePending(type);
        return eventRegister.registerEagerAddAction(type, decorate(action));
    }

    public Action whenObjectRemoved(Action action) {
        eventRegister.registerRemoveAction(type, decorate(action));
        return action;
    }

    public void whenObjectRemoved(Closure action) {
        whenObjectRemoved(toAction(action));
    }

    private Action decorate(Action action) {
        return eventRegister.getDecorator().decorate(action);
    }

    private Action toAction(Closure action) {
        return ConfigureUtil.configureUsing(action);
    }

    public boolean add(T toAdd) {
        assertMutable("add(T)");
        assertMutableCollectionContents();
        return doAdd(toAdd, eventRegister.getAddActions());
    }

    protected  boolean add(I toAdd, Action notification) {
        assertMutableCollectionContents();
        return doAdd(toAdd, notification);
    }

    protected  boolean doAdd(I toAdd, Action notification) {
        if (getStore().add(toAdd)) {
            didAdd(toAdd);
            notification.execute(toAdd);
            return true;
        } else {
            return false;
        }
    }

    private  boolean doAddRealized(I toAdd, Action notification) {
        if (getStore().addRealized(toAdd)) {
            didAdd(toAdd);
            notification.execute(toAdd);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void addLater(Provider provider) {
        assertMutable("addLater(Provider)");
        assertMutableCollectionContents();
        ProviderInternal providerInternal = Providers.internal(provider);
        store.addPending(providerInternal);
        if (eventRegister.isSubscribed(providerInternal.getType())) {
            doAddRealized(provider.get(), eventRegister.getAddActions());
        }
    }

    @Override
    public void addAllLater(Provider> provider) {
        assertMutable("addAllLater(Provider)");
        assertMutableCollectionContents();
        CollectionProviderInternal> providerInternal = Cast.uncheckedCast(provider);
        store.addPendingCollection(providerInternal);
        if (eventRegister.isSubscribed(providerInternal.getElementType())) {
            for (T value : provider.get()) {
                doAddRealized(value, eventRegister.getAddActions());
            }
        }
    }

    protected void didAdd(T toAdd) {
    }

    public boolean addAll(Collection c) {
        assertMutable("addAll(Collection)");
        assertMutableCollectionContents();
        boolean changed = false;
        for (T o : c) {
            if (doAdd(o, eventRegister.getAddActions())) {
                changed = true;
            }
        }
        return changed;
    }

    public void clear() {
        assertMutable("clear()");
        assertMutableCollectionContents();
        if (store.constantTimeIsEmpty()) {
            return;
        }
        List c = Lists.newArrayList(store.iteratorNoFlush());
        getStore().clear();
        for (T o : c) {
            eventRegister.fireObjectRemoved(o);
        }
    }

    public boolean contains(Object o) {
        return getStore().contains(o);
    }

    public boolean containsAll(Collection c) {
        return getStore().containsAll(c);
    }

    public boolean isEmpty() {
        return getStore().isEmpty();
    }

    public boolean remove(Object o) {
        assertMutable("remove(Object)");
        assertMutableCollectionContents();
        return doRemove(o);
    }

    private boolean doRemove(Object o) {
        if (o instanceof ProviderInternal) {
            ProviderInternal providerInternal = Cast.uncheckedCast(o);
            if (getStore().removePending(providerInternal)) {
                // NOTE: When removing provider, we don't need to fireObjectRemoved as they were never added in the first place.
                didRemove(providerInternal);
                return true;
            } else if (getType().isAssignableFrom(providerInternal.getType()) && providerInternal.isPresent()) {
                // The provider is of compatible type and the element was either already realized or we are removing a provider to the element
                o = providerInternal.get();
            }
            // Else, the provider is of incompatible type, maybe we have a domain object collection of Provider, fallthrough
        }

        if (getStore().remove(o)) {
            @SuppressWarnings("unchecked") T cast = (T) o;
            didRemove(cast);
            eventRegister.fireObjectRemoved(cast);
            return true;
        } else {
            return false;
        }
    }

    protected void didRemove(T t) {
    }

    protected void didRemove(ProviderInternal t) {
    }

    public boolean removeAll(Collection c) {
        assertMutable("removeAll(Collection)");
        assertMutableCollectionContents();
        if (store.constantTimeIsEmpty()) {
            return false;
        }
        boolean changed = false;
        for (Object o : c) {
            if (doRemove(o)) {
                changed = true;
            }
        }
        return changed;
    }

    public boolean retainAll(Collection target) {
        assertMutable("retainAll(Collection)");
        assertMutableCollectionContents();
        Object[] existingItems = toArray();
        boolean changed = false;
        for (Object existingItem : existingItems) {
            if (!target.contains(existingItem)) {
                doRemove(existingItem);
                changed = true;
            }
        }
        return changed;
    }

    public int size() {
        return store.size();
    }

    @Override
    public int estimatedSize() {
        return store.estimatedSize();
    }

    @Override
    public MutationGuard getMutationGuard() {
        return store.getMutationGuard();
    }

    public Collection findAll(Closure cl) {
        return findAll(cl, new ArrayList());
    }

    protected > S findAll(Closure cl, S matches) {
        if (store.constantTimeIsEmpty()) {
            return matches;
        }
        for (T t : filteredStore(createFilter(Specs.convertClosureToSpec(cl)))) {
            matches.add(t);
        }
        return matches;
    }

    /**
     * Asserts that the container can be modified in any way by the given method.
     */
    protected final void assertMutable(String methodName) {
        getMutationGuard().assertMutationAllowed(methodName, this);
    }

    /**
     * Subclasses may override this method to prevent add/remove methods.
     *
     * @see DefaultDomainObjectSet
     */
    protected void assertMutableCollectionContents() {
        // no special validation
    }

    protected class IteratorImpl implements Iterator, WithEstimatedSize {
        private final Iterator iterator;
        private T currentElement;

        public IteratorImpl(Iterator iterator) {
            this.iterator = iterator;
        }

        public boolean hasNext() {
            return iterator.hasNext();
        }

        public T next() {
            currentElement = iterator.next();
            return currentElement;
        }

        public void remove() {
            assertMutable("iterator().remove()");
            assertMutableCollectionContents();
            iterator.remove();
            didRemove(currentElement);
            getEventRegister().fireObjectRemoved(currentElement);
            currentElement = null;
        }

        @Override
        public int estimatedSize() {
            return DefaultDomainObjectCollection.this.estimatedSize();
        }
    }


}