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

org.gradle.language.internal.DefaultBinaryCollection 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.language.internal;

import com.google.common.collect.ImmutableSet;
import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.component.SoftwareComponent;
import org.gradle.api.internal.provider.AbstractReadOnlyProvider;
import org.gradle.api.provider.ProviderFactory;
import org.gradle.api.specs.Spec;
import org.gradle.internal.ImmutableActionSet;
import org.gradle.language.BinaryCollection;
import org.gradle.language.BinaryProvider;
import org.gradle.util.ConfigureUtil;

import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

// TODO - error messages
// TODO - display names for this container and the Provider implementations
public class DefaultBinaryCollection implements BinaryCollection {
    private enum State {
        Collecting, Realizing, Finalized
    }

    private final Class elementType;
    private final ProviderFactory providerFactory;
    private final Set elements = new LinkedHashSet();
    private List> pending = new LinkedList>();
    private State state = State.Collecting;
    private ImmutableActionSet knownActions = ImmutableActionSet.empty();
    private ImmutableActionSet configureActions = ImmutableActionSet.empty();
    private ImmutableActionSet finalizeActions = ImmutableActionSet.empty();

    @Inject
    public DefaultBinaryCollection(Class elementType, ProviderFactory providerFactory) {
        this.elementType = elementType;
        this.providerFactory = providerFactory;
    }

    @Override
    public  BinaryProvider get(final Class type, final Spec spec) {
        SingleElementProvider provider = new SingleElementProvider(type, spec);
        if (state == State.Collecting) {
            pending.add(provider);
        } else {
            provider.selectNow();
        }
        return provider;
    }

    @Override
    public BinaryProvider getByName(final String name) {
        return get(elementType, new Spec() {
            @Override
            public boolean isSatisfiedBy(T element) {
                return element.getName().equals(name);
            }
        });
    }

    @Override
    public BinaryProvider get(Spec spec) {
        return get(elementType, spec);
    }

    @Override
    public void whenElementKnown(Action action) {
        if (state != State.Collecting) {
            throw new IllegalStateException("Cannot add actions to this collection as it has already been realized.");
        }
        knownActions = knownActions.add(action);
    }

    @Override
    public  void whenElementKnown(final Class type, final Action action) {
        whenElementKnown(new TypeFilteringAction(type, action));
    }

    @Override
    public void whenElementFinalized(Action action) {
        if (state == State.Finalized) {
            for (T element : elements) {
                action.execute(element);
            }
        } else {
            finalizeActions = finalizeActions.add(action);
        }
    }

    @Override
    public  void whenElementFinalized(Class type, Action action) {
        whenElementFinalized(new TypeFilteringAction(type, action));
    }

    @Override
    public void configureEach(Action action) {
        if (state != State.Collecting) {
            throw new IllegalStateException("Cannot add actions to this collection as it has already been realized.");
        }
        configureActions = configureActions.add(action);
    }

    @Override
    public  void configureEach(Class type, Action action) {
        configureEach(new TypeFilteringAction(type, action));
    }

    public void add(T element) {
        if (state != State.Collecting) {
            throw new IllegalStateException("Cannot add an element to this collection as it has already been realized.");
        }
        elements.add(element);
    }

    /**
     * Realizes the contents of this collection, running configuration actions and firing notifications. No further elements can be added.
     */
    public void realizeNow() {
        if (state != State.Collecting) {
            throw new IllegalStateException("Cannot realize this collection as it has already been realized.");
        }
        state = State.Realizing;

        for (T element : elements) {
            knownActions.execute(element);
        }
        knownActions = ImmutableActionSet.empty();

        for (SingleElementProvider provider : pending) {
            provider.selectNow();
        }
        pending = null;

        for (T element : elements) {
            configureActions.execute(element);
        }
        configureActions = ImmutableActionSet.empty();
        state = State.Finalized;
        for (T element : elements) {
            finalizeActions.execute(element);
        }
        finalizeActions = ImmutableActionSet.empty();
    }

    @Override
    public Set get() {
        if (state != State.Finalized) {
            throw new IllegalStateException("Cannot query the elements of this container as the elements have not been created yet.");
        }
        return ImmutableSet.copyOf(elements);
    }

    private class SingleElementProvider extends AbstractReadOnlyProvider implements BinaryProvider {
        private final Class type;
        private Spec spec;
        private S match;
        private boolean ambiguous;

        SingleElementProvider(Class type, Spec spec) {
            this.type = type;
            this.spec = spec;
        }

        void selectNow() {
            for (T element : elements) {
                if (type.isInstance(element) && spec.isSatisfiedBy(type.cast(element))) {
                    if (match != null) {
                        ambiguous = true;
                        match = null;
                        break;
                    }
                    match = type.cast(element);
                }
            }
            spec = null;
        }

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

        // Mix in some Groovy DSL support. Should decorate instead
        public void configure(Closure closure) {
            configure(ConfigureUtil.configureUsing(closure));
        }

        @Override
        public void configure(final Action action) {
            configureEach(new Action() {
                @Override
                public void execute(T t) {
                    if (match == t) {
                        action.execute(match);
                    }
                }
            });
        }

        @Override
        public void whenFinalized(final Action action) {
            whenElementFinalized(new Action() {
                @Override
                public void execute(T t) {
                    if (match == t) {
                        action.execute(match);
                    }
                }
            });
        }

        @Nullable
        @Override
        public S getOrNull() {
            if (ambiguous) {
                throw new IllegalStateException("Found multiple elements");
            }
            return match;
        }
    }

    private static class TypeFilteringAction implements Action {
        private final Class type;
        private final Action action;

        TypeFilteringAction(Class type, Action action) {
            this.type = type;
            this.action = action;
        }

        @Override
        public void execute(T t) {
            if (type.isInstance(t)) {
                action.execute(type.cast(t));
            }
        }
    }
}