org.gradle.model.internal.inspect.StructNodeInitializer Maven / Gradle / Ivy
Show all versions of gradle-api Show documentation
/*
* Copyright 2016 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.inspect;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import org.gradle.api.Named;
import org.gradle.internal.BiAction;
import org.gradle.internal.typeconversion.TypeConverter;
import org.gradle.model.internal.core.*;
import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor;
import org.gradle.model.internal.manage.binding.ManagedProperty;
import org.gradle.model.internal.manage.binding.StructBindings;
import org.gradle.model.internal.manage.instance.ManagedProxyFactory;
import org.gradle.model.internal.manage.projection.ManagedModelProjection;
import org.gradle.model.internal.manage.schema.*;
import org.gradle.model.internal.type.ModelType;
import java.util.Arrays;
import java.util.List;
import static org.gradle.model.internal.core.ModelViews.getInstance;
import static org.gradle.model.internal.core.NodeInitializerContext.forProperty;
public class StructNodeInitializer implements NodeInitializer {
protected final StructBindings bindings;
public StructNodeInitializer(StructBindings bindings) {
this.bindings = bindings;
}
@Override
public Multimap getActions(ModelReference> subject, ModelRuleDescriptor descriptor) {
return ImmutableSetMultimap.builder()
.put(ModelActionRole.Discover, DirectNodeInputUsingModelAction.of(subject, descriptor,
Arrays.>asList(
ModelReference.of(ManagedProxyFactory.class),
ModelReference.of(TypeConverter.class)
),
new BiAction>>() {
@Override
public void execute(MutableModelNode modelNode, List> modelViews) {
ManagedProxyFactory proxyFactory = getInstance(modelViews.get(0), ManagedProxyFactory.class);
TypeConverter typeConverter = getInstance(modelViews, 1, TypeConverter.class);
for (StructSchema> viewSchema : bindings.getDeclaredViewSchemas()) {
addProjection(modelNode, viewSchema, proxyFactory, typeConverter);
}
modelNode.addProjection(new ModelElementProjection(bindings.getPublicSchema().getType()));
}
}
))
.put(ModelActionRole.Create, DirectNodeInputUsingModelAction.of(subject, descriptor,
Arrays.>asList(
ModelReference.of(ModelSchemaStore.class),
ModelReference.of(NodeInitializerRegistry.class)
),
new BiAction>>() {
@Override
public void execute(MutableModelNode modelNode, List> modelViews) {
ModelSchemaStore schemaStore = getInstance(modelViews, 0, ModelSchemaStore.class);
NodeInitializerRegistry nodeInitializerRegistry = getInstance(modelViews, 1, NodeInitializerRegistry.class);
addPropertyLinks(modelNode, schemaStore, nodeInitializerRegistry);
initializePrivateData(modelNode);
}
}
))
.build();
}
protected void initializePrivateData(MutableModelNode modelNode) {
}
private void addProjection(MutableModelNode modelNode, StructSchema viewSchema, ManagedProxyFactory proxyFactory, TypeConverter typeConverter) {
modelNode.addProjection(new ManagedModelProjection(viewSchema, bindings, proxyFactory, typeConverter));
}
private void addPropertyLinks(MutableModelNode modelNode,
ModelSchemaStore schemaStore,
NodeInitializerRegistry nodeInitializerRegistry
) {
for (ManagedProperty> property : bindings.getManagedProperties().values()) {
addPropertyLink(modelNode, property, schemaStore, nodeInitializerRegistry);
}
if (isNamedType()) {
// Only initialize "name" child node if the schema has such a managed property.
// This is not the case for a managed subtype of an unmanaged type that implements Named.
if (bindings.getManagedProperties().containsKey("name")) {
MutableModelNode nameLink = modelNode.getLink("name");
if (nameLink == null) {
throw new IllegalStateException("expected name node for " + modelNode.getPath());
}
nameLink.setPrivateData(ModelType.of(String.class), modelNode.getPath().getName());
}
}
}
private void addPropertyLink(MutableModelNode modelNode,
ManagedProperty
property,
ModelSchemaStore schemaStore,
NodeInitializerRegistry nodeInitializerRegistry
) {
ModelType
propertyType = property.getType();
ModelSchema
propertySchema = schemaStore.getSchema(propertyType);
ModelType publicType = bindings.getPublicSchema().getType();
validateProperty(propertySchema, property, nodeInitializerRegistry);
ModelPath childPath = modelNode.getPath().child(property.getName());
if (propertySchema instanceof ManagedImplSchema) {
if (!property.isWritable() || propertySchema instanceof ScalarCollectionSchema) {
ModelRegistrations.Builder builder = managedRegistrationBuilder(childPath, property, nodeInitializerRegistry, publicType);
addLink(modelNode, builder, property.isInternal());
} else {
// A nullable reference
modelNode.addReference(property.getName(), propertyType, null, modelNode.getDescriptor());
}
} else {
ModelRegistrations.Builder registrationBuilder;
if (shouldHaveANodeInitializer(property, propertySchema)) {
registrationBuilder = managedRegistrationBuilder(childPath, property, nodeInitializerRegistry, publicType);
} else {
registrationBuilder = ModelRegistrations.of(childPath);
}
registrationBuilder.withProjection(new UnmanagedModelProjection(propertyType));
registrationBuilder.withProjection(new ModelElementProjection(propertyType));
addLink(modelNode, registrationBuilder, property.isInternal());
}
}
private static
ModelRegistrations.Builder managedRegistrationBuilder(ModelPath childPath, ManagedProperty
property, NodeInitializerRegistry nodeInitializerRegistry, ModelType> publicType) {
return ModelRegistrations.of(childPath, nodeInitializerRegistry.getNodeInitializer(forProperty(property.getType(), property, publicType)));
}
private void addLink(MutableModelNode modelNode, ModelRegistrations.Builder builder, boolean internal) {
ModelRegistration registration = builder
.descriptor(modelNode.getDescriptor())
.hidden(internal)
.build();
modelNode.addLink(registration);
}
private
void validateProperty(ModelSchema
propertySchema, ManagedProperty
property, NodeInitializerRegistry nodeInitializerRegistry) {
if (propertySchema instanceof ManagedImplSchema) {
if (!property.isWritable()) {
if (isCollectionOfManagedTypes(propertySchema)) {
CollectionSchema
propertyCollectionsSchema = (CollectionSchema
) propertySchema;
ModelType> elementType = propertyCollectionsSchema.getElementType();
nodeInitializerRegistry.ensureHasInitializer(forProperty(elementType, property, bindings.getPublicSchema().getType()));
}
if (property.isDeclaredAsHavingUnmanagedType()) {
throw new UnmanagedPropertyMissingSetterException(property.getName());
}
}
} else if (!shouldHaveANodeInitializer(property, propertySchema) && !property.isWritable() && !isNamePropertyOfANamedType(property)) {
throw new ReadonlyImmutableManagedPropertyException(bindings.getPublicSchema().getType(), property.getName(), property.getType());
}
}
private
boolean isCollectionOfManagedTypes(ModelSchema
propertySchema) {
return propertySchema instanceof CollectionSchema
&& !(propertySchema instanceof ScalarCollectionSchema);
}
private
boolean isNamePropertyOfANamedType(ManagedProperty
property) {
return isNamedType() && "name".equals(property.getName());
}
private boolean isNamedType() {
return Named.class.isAssignableFrom(bindings.getPublicSchema().getType().getRawClass());
}
private
boolean shouldHaveANodeInitializer(ManagedProperty
property, ModelSchema
propertySchema) {
return !(propertySchema instanceof ScalarValueSchema) && !property.isDeclaredAsHavingUnmanagedType();
}
}