/* * Copyright 2015 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.model.internal.core; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import org.gradle.api.*; import org.gradle.api.specs.Spec; import org.gradle.internal.Actions; import org.gradle.internal.Cast; import org.gradle.internal.Specs; import org.gradle.model.ModelMap; import org.gradle.model.RuleSource; import java.util.Collection; import java.util.Iterator; import java.util.Set; import static org.gradle.internal.Cast.uncheckedCast; public class DomainObjectCollectionBackedModelMap<T> extends ModelMapGroovyView<T> { private final String name; private final Class<T> elementType; private final DomainObjectCollection<T> collection; private final NamedEntityInstantiator<T> instantiator; private final org.gradle.api.Namer<? super T> namer; private final Action<? super T> onCreateAction; public DomainObjectCollectionBackedModelMap(String name, Class<T> elementType, DomainObjectCollection<T> backingCollection, NamedEntityInstantiator<T> instantiator, org.gradle.api.Namer<? super T> namer, Action<? super T> onCreateAction) { this.name = name; this.elementType = elementType; this.collection = backingCollection; this.instantiator = instantiator; this.namer = namer; this.onCreateAction = onCreateAction; } @Override public String getName() { return name; } @Override public String getDisplayName() { return collection.toString(); } private <S> ModelMap<S> toNonSubtypeMap(Class<S> type) { DomainObjectCollection<S> cast = toNonSubtype(type); org.gradle.api.Namer<S> castNamer = Cast.uncheckedCast(namer); return DomainObjectCollectionBackedModelMap.wrap(name, type, cast, NamedEntityInstantiators.nonSubtype(type, elementType), castNamer, Actions.doNothing()); } private <S> DomainObjectSet<S> toNonSubtype(final Class<S> type) { return uncheckedCast(collection.matching(Specs.isInstance(type))); } private <S extends T> ModelMap<S> toSubtypeMap(Class<S> itemSubtype) { NamedEntityInstantiator<S> instantiator = uncheckedCast(this.instantiator); return DomainObjectCollectionBackedModelMap.wrap(name, itemSubtype, collection.withType(itemSubtype), instantiator, namer, onCreateAction); } @Nullable @Override public T get(String name) { return Iterables.find(collection, new HasNamePredicate<T>(name, namer), null); } @Override public Set<String> keySet() { return Sets.newHashSet(Iterables.transform(collection, new ToName<T>(namer))); } @Override public <S> void withType(Class<S> type, Action<? super S> configAction) { toNonSubtype(type).all(configAction); } private static class HasNamePredicate<T> implements Predicate<T> { private final String name; private final org.gradle.api.Namer<? super T> namer; public HasNamePredicate(String name, org.gradle.api.Namer<? super T> namer) { this.name = name; this.namer = namer; } @Override public boolean apply(@Nullable T input) { return namer.determineName(input).equals(name); } } private static class ToName<T> implements Function<T, String> { private final org.gradle.api.Namer<? super T> namer; public ToName(org.gradle.api.Namer<? super T> namer) { this.namer = namer; } @Override public String apply(@Nullable T input) { return namer.determineName(input); } } public static <T> DomainObjectCollectionBackedModelMap<T> wrap(String name, Class<T> elementType, DomainObjectCollection<T> domainObjectSet, NamedEntityInstantiator<T> instantiator, org.gradle.api.Namer<? super T> namer, Action<? super T> onCreate) { return new DomainObjectCollectionBackedModelMap<T>(name, elementType, domainObjectSet, instantiator, namer, onCreate); } @Override public int size() { return collection.size(); } @Override public boolean isEmpty() { return collection.isEmpty(); } @Nullable @Override public T get(Object name) { return get(name.toString()); } @Override public boolean containsKey(Object name) { return keySet().contains(name.toString()); } @Override public boolean containsValue(Object item) { //noinspection SuspiciousMethodCalls return collection.contains(item); } @Override public void create(String name) { create(name, elementType, Actions.doNothing()); } @Override public void create(String name, Action<? super T> configAction) { create(name, elementType, configAction); } @Override public <S extends T> void create(String name, Class<S> type) { create(name, type, Actions.doNothing()); } @Override public <S extends T> void create(String name, Class<S> type, Action<? super S> configAction) { if (containsKey(name)) { throw new IllegalStateException("Entry with name already exists: " + name); } S s = instantiator.create(name, type); configAction.execute(s); onCreateAction.execute(s); collection.add(s); } @Override public void put(String name, T instance) { throw new UnsupportedOperationException(); } @Override public void named(String name, Class<? extends RuleSource> ruleSource) { throw new UnsupportedOperationException(); } @Override public void all(Action<? super T> configAction) { collection.all(configAction); } @Override public void beforeEach(Action<? super T> configAction) { all(configAction); } @Override public <S> void withType(Class<S> type, Class<? extends RuleSource> rules) { throw new UnsupportedOperationException(); } @Override public void afterEach(Action<? super T> configAction) { all(configAction); } @Override public Collection<T> values() { return collection; } @Override public Iterator<T> iterator() { return collection.iterator(); } @Override public <S> void beforeEach(Class<S> type, Action<? super S> configAction) { withType(type, configAction); } @Override public <S> void afterEach(Class<S> type, Action<? super S> configAction) { withType(type, configAction); } @Override public void named(final String name, Action<? super T> configAction) { collection.matching(new Spec<T>() { @Override public boolean isSatisfiedBy(T element) { return get(name) == element; } }).all(configAction); } @Override public <S> ModelMap<S> withType(final Class<S> type) { if (type.equals(elementType)) { return uncheckedCast(this); } if (elementType.isAssignableFrom(type)) { Class<? extends T> castType = uncheckedCast(type); ModelMap<? extends T> subType = toSubtypeMap(castType); return uncheckedCast(subType); } return toNonSubtypeMap(type); } }