/*
* 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.collect.Iterables;
import com.google.common.collect.Lists;
import groovy.lang.Closure;
import org.gradle.api.Action;
import org.gradle.api.internal.ClosureBackedAction;
import org.gradle.api.specs.Specs;
import org.gradle.model.ModelSet;
import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
import org.gradle.model.internal.manage.instance.ManagedInstance;
import org.gradle.model.internal.type.ModelType;
import java.util.Collection;
import java.util.Iterator;
import static org.gradle.model.internal.core.NodePredicate.allLinks;
public class NodeBackedModelSet<T> implements ModelSet<T>, ManagedInstance {
private final ModelType<T> elementType;
private final ModelRuleDescriptor descriptor;
private final MutableModelNode modelNode;
private final ModelViewState state;
private final ChildNodeInitializerStrategy<T> creatorStrategy;
private final ModelReference<T> elementTypeReference;
private final ModelType<?> publicType;
private Collection<T> elements;
public NodeBackedModelSet(ModelType<?> publicType, ModelType<T> elementType, ModelRuleDescriptor descriptor, MutableModelNode modelNode, ModelViewState state, ChildNodeInitializerStrategy<T> creatorStrategy) {
this.publicType = publicType;
this.elementType = elementType;
this.elementTypeReference = ModelReference.of(elementType);
this.descriptor = descriptor;
this.modelNode = modelNode;
this.state = state;
this.creatorStrategy = creatorStrategy;
}
@Override
public MutableModelNode getBackingNode() {
return modelNode;
}
@Override
public ModelType<?> getManagedType() {
return ModelType.of(this.getClass());
}
@Override
public String getName() {
return modelNode.getPath().getName();
}
@Override
public String getDisplayName() {
return publicType.getDisplayName() + " '" + modelNode.getPath() + "'";
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public void create(final Action<? super T> action) {
state.assertCanMutate();
String name = String.valueOf(modelNode.getLinkCount(ModelNodes.withType(elementType)));
ModelPath childPath = modelNode.getPath().child(name);
final ModelRuleDescriptor descriptor = this.descriptor.append("create()");
NodeInitializer nodeInitializer = creatorStrategy.initializer(elementType, Specs.<ModelType<?>>satisfyAll());
ModelRegistration registration = ModelRegistrations.of(childPath, nodeInitializer)
.descriptor(descriptor)
.action(ModelActionRole.Initialize, NoInputsModelAction.of(ModelReference.of(childPath, elementType), descriptor, action))
.build();
modelNode.addLink(registration);
}
@Override
public void afterEach(Action<? super T> configAction) {
state.assertCanMutate();
modelNode.applyTo(allLinks(), ModelActionRole.Finalize, NoInputsModelAction.of(elementTypeReference, descriptor.append("afterEach()"), configAction));
}
@Override
public void beforeEach(Action<? super T> configAction) {
state.assertCanMutate();
modelNode.applyTo(allLinks(), ModelActionRole.Defaults, NoInputsModelAction.of(elementTypeReference, descriptor.append("afterEach()"), configAction));
}
@Override
public int size() {
state.assertCanReadChildren();
return modelNode.getLinkCount(ModelNodes.withType(elementType));
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean contains(Object o) {
return getElements().contains(o);
}
@Override
public Iterator<T> iterator() {
return getElements().iterator();
}
@Override
public Object[] toArray() {
return getElements().toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return getElements().toArray(a);
}
@Override
public boolean add(T e) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection<?> c) {
return getElements().containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
// TODO - mix this in using decoration. Also validate closure parameter types, if declared
public void create(Closure<?> closure) {
create(ClosureBackedAction.of(closure));
}
public void afterEach(Closure<?> closure) {
afterEach(ClosureBackedAction.of(closure));
}
public void beforeEach(Closure<?> closure) {
beforeEach(ClosureBackedAction.of(closure));
}
private Collection<T> getElements() {
state.assertCanReadChildren();
if (elements == null) {
elements = Lists.newArrayList(
Iterables.transform(modelNode.getLinks(ModelNodes.withType(elementType)), new Function<MutableModelNode, T>() {
@Override
public T apply(MutableModelNode input) {
return input.asImmutable(elementType, descriptor).getInstance();
}
})
);
}
return elements;
}
}