/* * 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.typeregistration; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.gradle.api.Nullable; import org.gradle.internal.Cast; import org.gradle.internal.reflect.Types.TypeVisitor; import org.gradle.model.Managed; import org.gradle.model.internal.core.MutableModelNode; import org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor; import org.gradle.model.internal.type.ModelType; import org.gradle.model.internal.type.ModelTypes; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import static org.gradle.internal.reflect.Types.walkTypeHierarchy; public class BaseInstanceFactory<PUBLIC> implements InstanceFactory<PUBLIC> { private final ModelType<PUBLIC> baseInterface; private final Map<ModelType<? extends PUBLIC>, TypeRegistration<? extends PUBLIC>> registrations = Maps.newLinkedHashMap(); private final Map<Class<?>, ImplementationFactory<? extends PUBLIC, ?>> factories = Maps.newLinkedHashMap(); public BaseInstanceFactory(Class<PUBLIC> baseInterface) { this.baseInterface = ModelType.of(baseInterface); } @Override public ModelType<PUBLIC> getBaseInterface() { return baseInterface; } public <S extends PUBLIC> void register(ModelType<S> publicType, Set<Class<?>> internalViews, @Nullable ModelType<?> implementationType, ModelRuleDescriptor definedBy) { TypeRegistrationBuilder<S> registration = register(publicType, definedBy); if (implementationType != null) { registration.withImplementation(implementationType); } for (Class<?> internalView : internalViews) { registration.withInternalView(ModelType.of(internalView)); } } /** * Registers a factory to use to create all implementation objects of the given type <em>and its subtypes</em>. The most specific match is used. */ public <S extends PUBLIC, I> void registerFactory(Class<I> implementationType, ImplementationFactory<S, I> implementationFactory) { factories.put(implementationType, implementationFactory); } public <S extends PUBLIC> TypeRegistrationBuilder<S> register(ModelType<S> publicType, ModelRuleDescriptor source) { TypeRegistration<S> registration = Cast.uncheckedCast(registrations.get(publicType)); if (registration == null) { registration = new TypeRegistration<S>(publicType); registrations.put(publicType, registration); } return new TypeRegistrationBuilderImpl<S>(source, registration); } @Override @Nullable public <S extends PUBLIC> ImplementationInfo getImplementationInfo(ModelType<S> publicType) { ImplementationRegistration<S> implementationRegistration = getImplementationRegistration(publicType); if (implementationRegistration == null) { return null; } return new ImplementationInfoImpl<S>(publicType, implementationRegistration, getInternalViews(publicType)); } @Override public <S extends PUBLIC> ImplementationInfo getManagedSubtypeImplementationInfo(final ModelType<S> publicType) { if (!isManaged(publicType)) { throw new IllegalArgumentException(String.format("Type '%s' is not managed", publicType)); } final AtomicReference<ImplementationInfoImpl<S>> implementationInfo = new AtomicReference<ImplementationInfoImpl<S>>(); walkTypeHierarchy(publicType.getConcreteClass(), new RegistrationHierarchyVisitor<S>() { @Override protected void visitRegistration(TypeRegistration<? extends PUBLIC> registration) { if (registration == null || registration.implementationRegistration == null) { return; } if (implementationInfo.get() == null || implementationInfo.get().implementationRegistration.implementationType.isAssignableFrom(registration.implementationRegistration.implementationType)) { implementationInfo.set(new ImplementationInfoImpl<S>(publicType, registration.implementationRegistration, getInternalViews(publicType))); } } }); if (implementationInfo.get() == null) { throw new IllegalStateException(String.format("Factory registration for '%s' is invalid because it doesn't extend an interface with a default implementation", publicType)); } return implementationInfo.get(); } private <S extends PUBLIC> Set<ModelType<?>> getInternalViews(ModelType<S> publicType) { final ImmutableSet.Builder<ModelType<?>> builder = ImmutableSet.builder(); walkTypeHierarchy(publicType.getConcreteClass(), new RegistrationHierarchyVisitor<S>() { @Override protected void visitRegistration(TypeRegistration<? extends PUBLIC> registration) { for (InternalViewRegistration<?> internalViewRegistration : registration.internalViewRegistrations) { builder.add(internalViewRegistration.getInternalView()); } } }); return builder.build(); } @Override public Set<ModelType<? extends PUBLIC>> getSupportedTypes() { ImmutableSortedSet.Builder<ModelType<? extends PUBLIC>> supportedTypes = ImmutableSortedSet.orderedBy(ModelTypes.<PUBLIC>displayOrder()); for (TypeRegistration<?> registration : registrations.values()) { if (registration.isConstructible()) { supportedTypes.add(registration.publicType); } } return supportedTypes.build(); } private <S extends PUBLIC> TypeRegistration<S> getRegistration(ModelType<S> type) { return Cast.uncheckedCast(registrations.get(type)); } private <S extends PUBLIC> ImplementationRegistration<S> getImplementationRegistration(ModelType<S> type) { TypeRegistration<S> registration = getRegistration(type); if (registration == null) { return null; } if (registration.implementationRegistration == null) { throw new IllegalArgumentException( String.format("Cannot create a '%s' because this type does not have an implementation registered.", type)); } return registration.implementationRegistration; } public void validateRegistrations() { for (TypeRegistration<? extends PUBLIC> registration : registrations.values()) { registration.validate(); } } private static boolean isManaged(ModelType<?> type) { return type.isAnnotationPresent(Managed.class); } public interface ImplementationFactory<PUBLIC, BASEIMPL> { <T extends BASEIMPL> T create(ModelType<? extends PUBLIC> publicType, ModelType<T> implementationType, String name, MutableModelNode node); } public interface TypeRegistrationBuilder<T> { TypeRegistrationBuilder<T> withImplementation(ModelType<?> implementationType); TypeRegistrationBuilder<T> withInternalView(ModelType<?> internalView); } private class TypeRegistrationBuilderImpl<S extends PUBLIC> implements TypeRegistrationBuilder<S> { private final ModelRuleDescriptor source; private final TypeRegistration<S> registration; public TypeRegistrationBuilderImpl(ModelRuleDescriptor source, TypeRegistration<S> registration) { this.source = source; this.registration = registration; } @Override public TypeRegistrationBuilder<S> withImplementation(ModelType<?> implementationType) { registration.setImplementation(implementationType, source); return this; } @Override public TypeRegistrationBuilder<S> withInternalView(ModelType<?> internalView) { registration.addInternalView(internalView, source); return this; } } private class TypeRegistration<S extends PUBLIC> { private final ModelType<S> publicType; private final boolean managedPublicType; private ImplementationRegistration<S> implementationRegistration; private final List<InternalViewRegistration<?>> internalViewRegistrations = Lists.newArrayList(); public TypeRegistration(ModelType<S> publicType) { this.publicType = publicType; this.managedPublicType = isManaged(publicType); } public void setImplementation(ModelType<?> implementationType, ModelRuleDescriptor source) { if (implementationRegistration != null) { throw new IllegalStateException(String.format("Cannot register implementation for type '%s' because an implementation for this type was already registered by %s", publicType, implementationRegistration.getSource())); } if (managedPublicType) { throw new IllegalArgumentException(String.format("Cannot specify default implementation for managed type '%s'", publicType)); } if (Modifier.isAbstract(implementationType.getConcreteClass().getModifiers())) { throw new IllegalArgumentException(String.format("Implementation type '%s' registered for '%s' must not be abstract", implementationType, publicType)); } try { implementationType.getConcreteClass().getConstructor(); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(String.format("Implementation type '%s' registered for '%s' must have a public default constructor", implementationType, publicType)); } Class<?> implementationClass = implementationType.getConcreteClass(); ImplementationFactory<S, ?> factory = findFactory(implementationClass); if (factory == null) { throw new IllegalArgumentException(String.format("No factory registered to create an instance of implementation class '%s'.", implementationType)); } this.implementationRegistration = new ImplementationRegistration<S>(source, implementationType, factory); } public <V> void addInternalView(ModelType<V> internalView, ModelRuleDescriptor source) { if (!internalView.getConcreteClass().isInterface()) { throw new IllegalArgumentException(String.format("Internal view '%s' registered for '%s' must be an interface", internalView, publicType)); } if (managedPublicType && !isManaged(internalView)) { throw new IllegalArgumentException(String.format("Internal view '%s' registered for managed type '%s' must be managed", internalView, publicType)); } internalViewRegistrations.add(new InternalViewRegistration<V>(source, internalView)); } public void validate() { if (managedPublicType) { validateManaged(); } else { validateUnmanaged(); } } public boolean isConstructible() { return managedPublicType || implementationRegistration != null; } private void validateManaged() { ImplementationInfo implementationInfo = getManagedSubtypeImplementationInfo(publicType); ModelType<?> delegateType = implementationInfo.getDelegateType(); for (InternalViewRegistration<?> internalViewRegistration : internalViewRegistrations) { validateManagedInternalView(internalViewRegistration, delegateType); } } private <V> void validateManagedInternalView(final InternalViewRegistration<V> internalViewRegistration, final ModelType<?> delegateType) { walkTypeHierarchy(internalViewRegistration.getInternalView().getConcreteClass(), new TypeVisitor<V>() { @Override public void visitType(Class<? super V> type) { if (!type.isAnnotationPresent(Managed.class) && !type.isAssignableFrom(delegateType.getConcreteClass())) { throw new IllegalStateException(String.format("Factory registration for '%s' is invalid because the default implementation type '%s' does not implement unmanaged internal view '%s', " + "internal view was registered by %s", publicType, delegateType, ModelType.of(type), internalViewRegistration.getSource())); } } }); } private void validateUnmanaged() { if (implementationRegistration == null) { return; } ModelType<?> implementationType = implementationRegistration.getImplementationType(); for (InternalViewRegistration<?> internalViewRegistration : internalViewRegistrations) { ModelType<?> internalView = internalViewRegistration.getInternalView(); // Managed internal views are allowed not to be implemented by the default implementation if (isManaged(internalView)) { continue; } if (!internalView.isAssignableFrom(implementationType)) { throw new IllegalStateException(String.format( "Factory registration for '%s' is invalid because the implementation type '%s' does not implement internal view '%s', " + "implementation type was registered by %s, " + "internal view was registered by %s", publicType, implementationType, internalView, implementationRegistration.getSource(), internalViewRegistration.getSource())); } } } } @Nullable private <S extends PUBLIC> ImplementationFactory<S, ?> findFactory(Class<?> implementationClass) { ImplementationFactory<? extends PUBLIC, ?> implementationFactory = factories.get(implementationClass); if (implementationFactory != null) { return Cast.uncheckedCast(implementationFactory); } Class<?> superclass = implementationClass.getSuperclass(); if (superclass != null && superclass != Object.class) { implementationFactory = findFactory(superclass); factories.put(implementationClass, implementationFactory); return Cast.uncheckedCast(implementationFactory); } return null; } private static class ImplementationRegistration<PUBLIC> { private final ModelRuleDescriptor source; private final ModelType<?> implementationType; private final ImplementationFactory<? super PUBLIC, ?> factory; private ImplementationRegistration(ModelRuleDescriptor source, ModelType<?> implementationType, ImplementationFactory<? super PUBLIC, ?> factory) { this.source = source; this.implementationType = implementationType; this.factory = factory; } public ModelRuleDescriptor getSource() { return source; } public ModelType<?> getImplementationType() { return implementationType; } public ImplementationFactory<? super PUBLIC, ?> getFactory() { return factory; } } private static class InternalViewRegistration<T> { private final ModelRuleDescriptor source; private final ModelType<T> internalView; private InternalViewRegistration(ModelRuleDescriptor source, ModelType<T> internalView) { this.source = source; this.internalView = internalView; } public ModelRuleDescriptor getSource() { return source; } public ModelType<T> getInternalView() { return internalView; } } private static class ImplementationInfoImpl<PUBLIC> implements ImplementationInfo { private final ModelType<PUBLIC> publicType; private final ImplementationRegistration<? super PUBLIC> implementationRegistration; private final Set<ModelType<?>> internalViews; public ImplementationInfoImpl(ModelType<PUBLIC> publicType, ImplementationRegistration<?> implementationRegistration, Set<ModelType<?>> internalViews) { this.publicType = publicType; this.internalViews = internalViews; this.implementationRegistration = Cast.uncheckedCast(implementationRegistration); } @Override public Object create(MutableModelNode modelNode) { ImplementationFactory<PUBLIC, Object> implementationFactory = Cast.uncheckedCast(implementationRegistration.factory); return implementationFactory.create(publicType, implementationRegistration.implementationType, modelNode.getPath().getName(), modelNode); } @Override public ModelType<?> getDelegateType() { return implementationRegistration.implementationType; } @Override public Set<ModelType<?>> getInternalViews() { return internalViews; } @Override public String toString() { return String.valueOf(publicType); } } private abstract class RegistrationHierarchyVisitor<S> implements TypeVisitor<S> { @Override public void visitType(Class<? super S> type) { if (!baseInterface.getConcreteClass().isAssignableFrom(type)) { return; } Class<? extends PUBLIC> superTypeClassAsBaseType = type.asSubclass(baseInterface.getConcreteClass()); ModelType<? extends PUBLIC> superTypeAsBaseType = ModelType.of(superTypeClassAsBaseType); TypeRegistration<? extends PUBLIC> registration = getRegistration(superTypeAsBaseType); if (registration != null) { visitRegistration(registration); } } protected abstract void visitRegistration(TypeRegistration<? extends PUBLIC> registration); } }