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.11.1
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 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.FilteredCollection;
import org.gradle.api.specs.Spec;
import org.gradle.api.specs.Specs;
import org.gradle.util.ConfigureUtil;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

public class DefaultDomainObjectCollection extends AbstractCollection implements DomainObjectCollection {

    private final Class type;
    private final CollectionEventRegister eventRegister;
    private final Collection store;
    private final Set mutateActions = new LinkedHashSet();

    public DefaultDomainObjectCollection(Class type, Collection store) {
        this(type, store, new CollectionEventRegister());
    }

    protected DefaultDomainObjectCollection(Class type, Collection store, CollectionEventRegister eventRegister) {
        this.type = type;
        this.store = store;
        this.eventRegister = eventRegister;
    }

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

    public Class getType() {
        return type;
    }

    protected Collection 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  Collection filteredStore(CollectionFilter filter) {
        return new FilteredCollection(this, filter);
    }

    protected  CollectionEventRegister filteredEvents(CollectionFilter filter) {
        return getEventRegister().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(getStore().iterator());
    }

    public void all(Action action) {
        action = whenObjectAdded(action);

        // copy in case any actions mutate the store
        // linked list because the underlying store may preserve order
        Collection copied = new LinkedList(this);

        for (T t : copied) {
            action.execute(t);
        }
    }

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

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

    public  DomainObjectCollection withType(Class type, Closure configureClosure) {
        DomainObjectCollection result = withType(type);
        result.all(configureClosure);
        return result;
    }

    public Action whenObjectAdded(Action action) {
        return eventRegister.registerAddAction(action);
    }

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

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

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

    /**
     * Adds an action which is executed before this collection is mutated. Any exception thrown by the action will veto the mutation.
     */
    public void beforeChange(Runnable action) {
        mutateActions.add(action);
    }

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

    public boolean add(T toAdd) {
        assertMutable();
        return doAdd(toAdd);
    }

    private boolean doAdd(T toAdd) {
        if (getStore().add(toAdd)) {
            didAdd(toAdd);
            eventRegister.getAddAction().execute(toAdd);
            return true;
        } else {
            return false;
        }
    }

    protected void didAdd(T toAdd) {
    }

    public boolean addAll(Collection c) {
        assertMutable();
        boolean changed = false;
        for (T o : c) {
            if (doAdd(o)) {
                changed = true;
            }
        }
        return changed;
    }

    public void clear() {
        assertMutable();
        Object[] c = toArray();
        getStore().clear();
        for (Object o : c) {
            eventRegister.getRemoveAction().execute((T) 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();
        return doRemove(o);
    }

    private boolean doRemove(Object o) {
        if (getStore().remove(o)) {
            @SuppressWarnings("unchecked") T cast = (T) o;
            didRemove(cast);
            eventRegister.getRemoveAction().execute(cast);
            return true;
        } else {
            return false;
        }
    }

    protected void didRemove(T t) {
    }

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

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

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

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

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

    protected void assertMutable() {
        for (Runnable action : mutateActions) {
            action.run();
        }
    }

    private class IteratorImpl implements Iterator {
        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();
            didRemove(currentElement);
            getEventRegister().getRemoveAction().execute(currentElement);
            currentElement = null;
        }
    }
}