public <T> T createProxy( ModelElementState state, StructSchema<T> viewSchema, @Nullable StructSchema<? extends T> delegateSchema, TypeConverter typeConverter) { try { Class<? extends T> generatedClass = getGeneratedImplementation(viewSchema, delegateSchema); if (generatedClass == null) { throw new IllegalStateException( "No managed implementation class available for: " + viewSchema.getType()); } if (delegateSchema == null) { Constructor<? extends T> constructor = generatedClass.getConstructor(ModelElementState.class, TypeConverter.class); return constructor.newInstance(state, typeConverter); } else { ModelType<? extends T> delegateType = delegateSchema.getType(); Object delegate = state.getBackingNode().getPrivateData(delegateType); Constructor<? extends T> constructor = generatedClass.getConstructor( ModelElementState.class, TypeConverter.class, delegateType.getConcreteClass()); return constructor.newInstance(state, typeConverter, delegate); } } catch (InvocationTargetException e) { throw UncheckedException.throwAsUncheckedException(e.getTargetException()); } catch (Exception e) { throw UncheckedException.throwAsUncheckedException(e); } }
@Override public <T> void extract(ModelSchemaExtractionContext<T> extractionContext) { Type type = extractionContext.getType().getType(); if (!(type instanceof Class)) { return; } Class<?> contractType = (Class<?>) type; if (!contractType.isInterface()) { return; } if (contractType.getGenericInterfaces().length != 1) { return; } Type superType = contractType.getGenericInterfaces()[0]; if (!(superType instanceof ParameterizedType)) { return; } ParameterizedType parameterizedSuperType = (ParameterizedType) superType; if (!parameterizedSuperType.getRawType().equals(ModelMap.class)) { return; } ModelType<?> elementType = ModelType.of(parameterizedSuperType.getActualTypeArguments()[0]); Class<?> proxyImpl = generator.generate(ModelMapGroovyDecorator.Managed.class, contractType); extractionContext.found( new SpecializedMapSchema<T>(extractionContext.getType(), elementType, proxyImpl)); }
private void generateProxyClass( ClassWriter visitor, StructSchema<?> viewSchema, StructSchema<?> delegateSchema, Collection<String> interfacesToImplement, Set<Class<?>> typesToDelegate, Type generatedType, Type superclassType) { ModelType<?> viewType = viewSchema.getType(); Class<?> viewClass = viewType.getConcreteClass(); declareClass(visitor, interfacesToImplement, generatedType, superclassType); declareStateField(visitor); declareTypeConverterField(visitor); declareManagedTypeField(visitor); declareCanCallSettersField(visitor); writeStaticConstructor(visitor, generatedType, viewClass); writeConstructor(visitor, generatedType, superclassType, delegateSchema); writeToString(visitor, generatedType, viewClass, delegateSchema); writeManagedInstanceMethods(visitor, generatedType); if (delegateSchema != null) { declareDelegateField(visitor, delegateSchema); writeDelegateMethods(visitor, generatedType, delegateSchema, typesToDelegate); } writeGroovyMethods(visitor, viewClass); writePropertyMethods(visitor, generatedType, viewSchema, delegateSchema); writeHashCodeMethod(visitor, generatedType); writeEqualsMethod(visitor, generatedType); visitor.visitEnd(); }
/** * Base class that may be used for custom {@link GeneralComponentSpec} implementations. However, it * is generally better to use an interface annotated with {@link org.gradle.model.Managed} and not * use an implementation class at all. */ @Incubating public class BaseComponentSpec extends DefaultComponentSpec implements GeneralComponentSpec { private static final ModelType<BinarySpec> BINARY_SPEC_MODEL_TYPE = ModelType.of(BinarySpec.class); private static final ModelType<LanguageSourceSet> LANGUAGE_SOURCE_SET_MODEL_TYPE = ModelType.of(LanguageSourceSet.class); private final MutableModelNode binaries; private final MutableModelNode sources; public BaseComponentSpec() { MutableModelNode modelNode = getInfo().modelNode; binaries = ModelMaps.addModelMapNode(modelNode, BINARY_SPEC_MODEL_TYPE, "binaries"); sources = ModelMaps.addModelMapNode(modelNode, LANGUAGE_SOURCE_SET_MODEL_TYPE, "sources"); } @Override public ModelMap<LanguageSourceSet> getSources() { return ModelMaps.toView(sources, LANGUAGE_SOURCE_SET_MODEL_TYPE); } @Override public ModelMap<BinarySpec> getBinaries() { return ModelMaps.toView(binaries, BINARY_SPEC_MODEL_TYPE); } }
private <T extends ComponentSpec> void registerImplementation( ComponentSpecFactory components, List<ModelView<?>> inputs) { ServiceRegistry serviceRegistry = ModelViews.assertType(inputs.get(0), ModelType.of(ServiceRegistry.class)).getInstance(); final Instantiator instantiator = serviceRegistry.get(Instantiator.class); final ProjectIdentifier projectIdentifier = ModelViews.assertType(inputs.get(1), ModelType.of(ProjectIdentifier.class)).getInstance(); final ProjectSourceSet projectSourceSet = ModelViews.assertType(inputs.get(2), ModelType.of(ProjectSourceSet.class)).getInstance(); components.registerFactory( Cast.<ModelType<ComponentSpec>>uncheckedCast(publicType), descriptor, new BiFunction<ComponentSpec, String, MutableModelNode>() { @Override public ComponentSpec apply(String name, MutableModelNode modelNode) { ComponentSpecIdentifier id = new DefaultComponentSpecIdentifier(projectIdentifier.getPath(), name); return BaseComponentSpec.create( implementationType.getConcreteClass(), id, modelNode, projectSourceSet, instantiator); } }); components.registerImplementation( Cast.<ModelType<T>>uncheckedCast(publicType), descriptor, Cast.<ModelType<? extends T>>uncheckedCast(implementationType)); if (COMPONENT_SPEC_INTERNAL_MODEL_TYPE.isAssignableFrom(implementationType)) { components.registerInternalView(publicType, descriptor, COMPONENT_SPEC_INTERNAL_MODEL_TYPE); } }
protected void validateElementType(Object o) { if (o != null) { ModelType<?> obType = ModelType.of(o.getClass()); if (!obType.equals(elementType)) { throw new IllegalArgumentException( String.format( "Cannot add an element of type %s to a collection of %s", obType, elementType)); } } }
private static void maybeAppendConstructables( StringBuffer s, Iterable<ModelType<?>> constructableTypes, int pad) { if (!Iterables.isEmpty(constructableTypes)) { String padding = pad(pad); s.append(String.format("%n%s- or a type which Gradle is capable of constructing:", padding)); for (ModelType<?> modelType : constructableTypes) { s.append(String.format("%n %s- %s", padding, modelType.getName())); } } }
private void validateRuleMethod( MethodRuleDefinition<?, ?> ruleDefinition, Method ruleMethod, ValidationProblemCollector problems) { if (Modifier.isPrivate(ruleMethod.getModifiers())) { problems.add(ruleMethod, "A rule method cannot be private"); } if (Modifier.isAbstract(ruleMethod.getModifiers())) { problems.add(ruleMethod, "A rule method cannot be abstract"); } if (ruleMethod.getTypeParameters().length > 0) { problems.add(ruleMethod, "Cannot have type variables (i.e. cannot be a generic method)"); } // TODO validations on method: synthetic, bridge methods, varargs, abstract, native ModelType<?> returnType = ModelType.returnType(ruleMethod); if (returnType.isRawClassOfParameterizedType()) { problems.add( ruleMethod, "Raw type " + returnType + " used for return type (all type parameters must be specified of parameterized type)"); } for (int i = 0; i < ruleDefinition.getReferences().size(); i++) { ModelReference<?> reference = ruleDefinition.getReferences().get(i); if (reference.getType().isRawClassOfParameterizedType()) { problems.add( ruleMethod, "Raw type " + reference.getType() + " used for parameter " + (i + 1) + " (all type parameters must be specified of parameterized type)"); } if (reference.getPath() != null) { try { ModelPath.validatePath(reference.getPath().toString()); } catch (Exception e) { problems.add( ruleDefinition, "The declared model element path '" + reference.getPath() + "' used for parameter " + (i + 1) + " is not a valid path", e); } } } }
@Nullable public ModelType<?> getUpperBound() { WildcardWrapper wildcardType = getWildcardType(); if (wildcardType == null) { return null; } else { ModelType<?> upperBound = Simple.typed(wildcardType.getUpperBound()); if (upperBound.equals(UNTYPED)) { return null; } return upperBound; } }
public boolean isHasWildcardTypeVariables() { if (isWildcard()) { return true; } else if (isParameterized()) { for (ModelType<?> typeVariable : getTypeVariables()) { if (typeVariable.isHasWildcardTypeVariables()) { return true; } } } return false; }
/** * Casts this {@code ModelType} object to represent a subclass of the class represented by the * specified class object. Checks that the cast is valid, and throws a {@code ClassCastException} * if it is not. If this method succeeds, it always returns a reference to this {@code ModelType} * object. * * @throws ClassCastException if this cannot be cast as the subtype of the given type. * @throws IllegalStateException if this is a wildcard. * @throws IllegalArgumentException if the given type is a wildcard. */ public <U> ModelType<? extends U> asSubtype(ModelType<U> modelType) { if (isWildcard()) { throw new IllegalStateException(this + " is a wildcard type"); } if (modelType.isWildcard()) { throw new IllegalArgumentException(modelType + " is a wildcard type"); } if (modelType.getRawClass().isAssignableFrom(getRawClass())) { return Cast.uncheckedCast(this); } else { throw new ClassCastException( String.format("'%s' cannot be cast as a subtype of '%s'", this, modelType)); } }
private <T> CachedRuleSource doExtract(final Class<T> source) { final ModelType<T> type = ModelType.of(source); DefaultMethodModelRuleExtractionContext context = new DefaultMethodModelRuleExtractionContext(type, this); // TODO - exceptions thrown here should point to some extensive documentation on the concept of // class rule sources StructSchema<T> schema = getSchema(source, context); if (schema == null) { throw new InvalidModelRuleDeclarationException(context.problems.format()); } // sort for determinism Set<Method> methods = new TreeSet<Method>(Ordering.usingToString()); methods.addAll(Arrays.asList(source.getDeclaredMethods())); ImmutableList.Builder<ModelProperty<?>> implicitInputs = ImmutableList.builder(); ModelProperty<?> target = null; for (ModelProperty<?> property : schema.getProperties()) { if (property.isAnnotationPresent(RuleTarget.class)) { target = property; } else if (property.isAnnotationPresent(RuleInput.class) && !(property.getSchema() instanceof ScalarValueSchema)) { implicitInputs.add(property); } for (WeaklyTypeReferencingMethod<?, ?> method : property.getAccessors()) { methods.remove(method.getMethod()); } } ImmutableList.Builder<ExtractedRuleDetails> rules = ImmutableList.builder(); for (Method method : methods) { MethodRuleDefinition<?, ?> ruleDefinition = DefaultMethodRuleDefinition.create(source, method); ExtractedModelRule rule = getMethodHandler(ruleDefinition, method, context); if (rule != null) { rules.add(new ExtractedRuleDetails(ruleDefinition, rule)); } } if (context.hasProblems()) { throw new InvalidModelRuleDeclarationException(context.problems.format()); } StructBindings<T> bindings = structBindingsStore.getBindings(schema); if (schema.getProperties().isEmpty()) { return new StatelessRuleSource( rules.build(), Modifier.isAbstract(source.getModifiers()) ? new AbstractRuleSourceFactory<T>(schema, bindings, proxyFactory) : new ConcreteRuleSourceFactory<T>(type)); } else { return new ParameterizedRuleSource( rules.build(), target, implicitInputs.build(), schema, bindings, proxyFactory); } }
@Override protected void execute( MutableModelNode modelNode, ComponentSpecFactory components, List<ModelView<?>> inputs) { components.registerPublicType(publicType); if (implementationType != null) { registerImplementation(components, inputs); } for (Class<?> internalView : internalViews) { components.registerInternalView(publicType, descriptor, ModelType.of(internalView)); } }
@Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ListViewFactory<?> that = (ListViewFactory<?>) o; return elementType.equals(that.elementType); }
@Override protected <T, E> NodeInitializer extractNodeInitializer( ModelCollectionSchema<T, E> schema, NodeInitializerRegistry nodeInitializerRegistry) { ModelType<T> type = schema.getType(); Class<? super T> rawClass = type.getRawClass(); ModelType<? super T> rawCollectionType = ModelType.of(rawClass); if (TYPES.contains(rawCollectionType)) { if (schema.getType().getRawClass() == List.class) { return new ProjectionOnlyNodeInitializer( ScalarCollectionModelProjection.get( ModelTypes.list(schema.getElementType()), new ListViewFactory<E>(schema.getElementType()))); } else { return new ProjectionOnlyNodeInitializer( ScalarCollectionModelProjection.get( ModelTypes.set(schema.getElementType()), new SetViewFactory<E>(schema.getElementType()))); } } return null; }
@Override public T create() { Class<T> concreteClass = type.getConcreteClass(); try { Constructor<T> declaredConstructor = concreteClass.getDeclaredConstructor(); declaredConstructor.setAccessible(true); return declaredConstructor.newInstance(); } catch (InvocationTargetException e) { throw UncheckedException.throwAsUncheckedException(e.getTargetException()); } catch (Exception e) { throw UncheckedException.throwAsUncheckedException(e); } }
public <T> ModelSchemaExtractionResult<T> extract( ModelSchemaExtractionContext<T> extractionContext, ModelSchemaStore store, final ModelSchemaCache cache) { ModelType<T> type = extractionContext.getType(); if (MODEL_MAP_MODEL_TYPE.isAssignableFrom(type)) { if (!type.getRawClass().equals(ModelMap.class)) { throw new InvalidManagedModelElementTypeException( extractionContext, String.format("subtyping %s is not supported.", ModelMap.class.getName())); } if (type.isHasWildcardTypeVariables()) { throw new InvalidManagedModelElementTypeException( extractionContext, String.format("type parameter of %s cannot be a wildcard.", ModelMap.class.getName())); } List<ModelType<?>> typeVariables = type.getTypeVariables(); if (typeVariables.isEmpty()) { throw new InvalidManagedModelElementTypeException( extractionContext, String.format("type parameter of %s has to be specified.", ModelMap.class.getName())); } ModelType<?> elementType = typeVariables.get(0); if (MODEL_MAP_MODEL_TYPE.isAssignableFrom(elementType)) { throw new InvalidManagedModelElementTypeException( extractionContext, String.format( "%1$s cannot be used as type parameter of %1$s.", ModelMap.class.getName())); } return gettModelSchemaExtractionResult(extractionContext, cache, elementType, store); } else { return null; } }
@Override protected <R> ModelSchema<R> createSchema( ModelSchemaExtractionContext<R> extractionContext, ModelSchemaStore store, ModelType<R> type, List<ModelProperty<?>> properties, List<ModelSchemaAspect> aspects) { return new ModelManagedImplStructSchema<R>( type, properties, aspects, type.getConcreteClass(), delegateType, Functions.<NodeInitializer>constant(null)); }
public <T> T getPrivateData(ModelType<T> type) { if (privateData == null) { return null; } if (!type.isAssignableFrom(privateDataType)) { throw new ClassCastException( "Cannot get private data '" + privateData + "' of type '" + privateDataType + "' as type '" + type); } return Cast.uncheckedCast(privateData); }
/** Invoked by transformed DSL creation rules */ public <T> void create(String modelPathString, Class<T> type, Closure<?> closure) { SourceLocation sourceLocation = ruleLocationExtractor.transform(closure); ModelPath modelPath = ModelPath.path(modelPathString); ModelRuleDescriptor descriptor = toDescriptor(sourceLocation, modelPath); try { NodeInitializerRegistry nodeInitializerRegistry = modelRegistry.realize( DefaultNodeInitializerRegistry.DEFAULT_REFERENCE.getPath(), DefaultNodeInitializerRegistry.DEFAULT_REFERENCE.getType()); NodeInitializer nodeInitializer = nodeInitializerRegistry.getNodeInitializer(ModelType.of(type)); modelRegistry.create( ModelCreators.of(modelPath, nodeInitializer).descriptor(descriptor).build()); } catch (ModelTypeInitializationException e) { throw new InvalidModelRuleDeclarationException(descriptor, e); } registerAction(modelPath, type, descriptor, ModelActionRole.Initialize, closure); }
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()); } } }
@Override public <T> T getPrivateData(Class<T> type) { return getPrivateData(ModelType.of(type)); }
@Override public <T> void setPrivateData(Class<? super T> type, T object) { setPrivateData(ModelType.of(type), object); }
private static class RegistrationAction extends AbstractModelActionWithView<ComponentSpecFactory> { private static final ModelType<ComponentSpecInternal> COMPONENT_SPEC_INTERNAL_MODEL_TYPE = ModelType.of(ComponentSpecInternal.class); private final ModelType<? extends ComponentSpec> publicType; private final ModelType<? extends BaseComponentSpec> implementationType; private final Set<Class<?>> internalViews; public RegistrationAction( ModelType<? extends ComponentSpec> publicType, ModelType<? extends BaseComponentSpec> implementationType, Set<Class<?>> internalViews, ModelRuleDescriptor descriptor) { super( ModelReference.of(ComponentSpecFactory.class), descriptor, ModelReference.of("serviceRegistry", ServiceRegistry.class), ModelReference.of("projectIdentifier", ProjectIdentifier.class), ModelReference.of("sources", ProjectSourceSet.class)); this.publicType = publicType; this.implementationType = implementationType; this.internalViews = internalViews; } @Override protected void execute( MutableModelNode modelNode, ComponentSpecFactory components, List<ModelView<?>> inputs) { components.registerPublicType(publicType); if (implementationType != null) { registerImplementation(components, inputs); } for (Class<?> internalView : internalViews) { components.registerInternalView(publicType, descriptor, ModelType.of(internalView)); } } private <T extends ComponentSpec> void registerImplementation( ComponentSpecFactory components, List<ModelView<?>> inputs) { ServiceRegistry serviceRegistry = ModelViews.assertType(inputs.get(0), ModelType.of(ServiceRegistry.class)).getInstance(); final Instantiator instantiator = serviceRegistry.get(Instantiator.class); final ProjectIdentifier projectIdentifier = ModelViews.assertType(inputs.get(1), ModelType.of(ProjectIdentifier.class)).getInstance(); final ProjectSourceSet projectSourceSet = ModelViews.assertType(inputs.get(2), ModelType.of(ProjectSourceSet.class)).getInstance(); components.registerFactory( Cast.<ModelType<ComponentSpec>>uncheckedCast(publicType), descriptor, new BiFunction<ComponentSpec, String, MutableModelNode>() { @Override public ComponentSpec apply(String name, MutableModelNode modelNode) { ComponentSpecIdentifier id = new DefaultComponentSpecIdentifier(projectIdentifier.getPath(), name); return BaseComponentSpec.create( implementationType.getConcreteClass(), id, modelNode, projectSourceSet, instantiator); } }); components.registerImplementation( Cast.<ModelType<T>>uncheckedCast(publicType), descriptor, Cast.<ModelType<? extends T>>uncheckedCast(implementationType)); if (COMPONENT_SPEC_INTERNAL_MODEL_TYPE.isAssignableFrom(implementationType)) { components.registerInternalView(publicType, descriptor, COMPONENT_SPEC_INTERNAL_MODEL_TYPE); } } }
public class ScalarCollectionNodeInitializerExtractionStrategy extends CollectionNodeInitializerExtractionSupport { public static final List<ModelType<?>> TYPES = ImmutableList.<ModelType<?>>of(ModelType.of(List.class), ModelType.of(Set.class)); @Override protected <T, E> NodeInitializer extractNodeInitializer( ModelCollectionSchema<T, E> schema, NodeInitializerRegistry nodeInitializerRegistry) { ModelType<T> type = schema.getType(); Class<? super T> rawClass = type.getRawClass(); ModelType<? super T> rawCollectionType = ModelType.of(rawClass); if (TYPES.contains(rawCollectionType)) { if (schema.getType().getRawClass() == List.class) { return new ProjectionOnlyNodeInitializer( ScalarCollectionModelProjection.get( ModelTypes.list(schema.getElementType()), new ListViewFactory<E>(schema.getElementType()))); } else { return new ProjectionOnlyNodeInitializer( ScalarCollectionModelProjection.get( ModelTypes.set(schema.getElementType()), new SetViewFactory<E>(schema.getElementType()))); } } return null; } private static class ScalarCollectionModelProjection<E> extends TypedModelProjection<E> { public static <E, U extends Collection<E>> ScalarCollectionModelProjection<U> get( ModelType<U> type, ModelViewFactory<U> viewFactory) { return new ScalarCollectionModelProjection<U>(type, viewFactory); } public ScalarCollectionModelProjection(ModelType<E> type, ModelViewFactory<E> viewFactory) { super(type, viewFactory, true, true); } @Override public Optional<String> getValueDescription(MutableModelNode modelNodeInternal) { Collection<?> values = ScalarCollectionSchema.get(modelNodeInternal); if (values == null) { return Optional.absent(); } return Optional.of(values.toString()); } } public static class ListViewFactory<T> implements ModelViewFactory<List<T>> { private final ModelType<T> elementType; public ListViewFactory(ModelType<T> elementType) { this.elementType = elementType; } @Override public ModelView<List<T>> toView( MutableModelNode modelNode, ModelRuleDescriptor ruleDescriptor, boolean writable) { ModelType<List<T>> listType = ModelTypes.list(elementType); DefaultModelViewState state = new DefaultModelViewState(listType, ruleDescriptor, writable, !writable); ListBackedCollection<T> list = new ListBackedCollection<T>(modelNode, state, elementType); return InstanceModelView.of(modelNode.getPath(), listType, list, state.closer()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ListViewFactory<?> that = (ListViewFactory<?>) o; return elementType.equals(that.elementType); } @Override public int hashCode() { return elementType.hashCode(); } } public static class SetViewFactory<T> implements ModelViewFactory<Set<T>> { private final ModelType<T> elementType; public SetViewFactory(ModelType<T> elementType) { this.elementType = elementType; } @Override public ModelView<Set<T>> toView( MutableModelNode modelNode, ModelRuleDescriptor ruleDescriptor, boolean writable) { ModelType<Set<T>> setType = ModelTypes.set(elementType); DefaultModelViewState state = new DefaultModelViewState(setType, ruleDescriptor, writable, !writable); SetBackedCollection<T> set = new SetBackedCollection<T>(modelNode, state, elementType); return InstanceModelView.of(modelNode.getPath(), setType, set, state.closer()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ListViewFactory<?> that = (ListViewFactory<?>) o; return elementType.equals(that.elementType); } @Override public int hashCode() { return elementType.hashCode(); } } private abstract static class NodeBackedCollection<T, C extends Collection<T>> implements Collection<T>, ManagedInstance { private final MutableModelNode modelNode; private final ModelViewState state; private final ModelType<T> elementType; public NodeBackedCollection( MutableModelNode modelNode, ModelViewState state, ModelType<T> elementType) { this.modelNode = modelNode; this.state = state; this.elementType = elementType; } protected C getDelegate(boolean write) { if (write) { state.assertCanMutate(); } Collection<T> delegate = Cast.uncheckedCast(ScalarCollectionSchema.get(modelNode)); return initialValue(write, delegate); } protected abstract C createPrivateData(boolean mutable); private C initialValue(boolean write, Collection<T> delegate) { if (delegate == null) { if (write) { delegate = createPrivateData(true); ScalarCollectionSchema.set(modelNode, delegate); } else { delegate = createPrivateData(false); } } return Cast.uncheckedCast(delegate); } @Override public MutableModelNode getBackingNode() { return modelNode; } @Override public ModelType<?> getManagedType() { return ModelType.of(this.getClass()); } protected void validateElementType(Object o) { if (o != null) { ModelType<?> obType = ModelType.of(o.getClass()); if (!obType.equals(elementType)) { throw new IllegalArgumentException( String.format( "Cannot add an element of type %s to a collection of %s", obType, elementType)); } } } protected void validateCollection(Collection<? extends T> c) { for (T element : c) { validateElementType(element); } } @Override public boolean add(T t) { validateElementType(t); return getDelegate(true).add(t); } @Override public boolean addAll(Collection<? extends T> c) { validateCollection(c); return getDelegate(true).addAll(c); } @Override public void clear() { getDelegate(true).clear(); } @Override public boolean contains(Object o) { return getDelegate(false).contains(o); } @Override public boolean containsAll(Collection<?> c) { return getDelegate(false).containsAll(c); } @Override public boolean equals(Object o) { return getDelegate(false).equals(o); } @Override public int hashCode() { return getDelegate(false).hashCode(); } @Override public boolean isEmpty() { return getDelegate(false).isEmpty(); } @Override public Iterator<T> iterator() { return new MutationSafeIterator(getDelegate(false).iterator()); } @Override public boolean remove(Object o) { return getDelegate(true).remove(o); } @Override public boolean removeAll(Collection<?> c) { return getDelegate(true).removeAll(c); } @Override public boolean retainAll(Collection<?> c) { return getDelegate(true).retainAll(c); } @Override public int size() { return getDelegate(false).size(); } @Override public Object[] toArray() { return getDelegate(false).toArray(); } @Override public <T> T[] toArray(T[] a) { return getDelegate(false).toArray(a); } private final class MutationSafeIterator implements Iterator<T> { private final Iterator<T> delegate; private MutationSafeIterator(Iterator<T> delegate) { this.delegate = delegate; } @Override public boolean hasNext() { return delegate.hasNext(); } @Override public T next() { return delegate.next(); } @Override public void remove() { state.assertCanMutate(); delegate.remove(); } } } private static class ListBackedCollection<T> extends NodeBackedCollection<T, List<T>> implements List<T> { public ListBackedCollection( MutableModelNode modelNode, ModelViewState state, ModelType<T> elementType) { super(modelNode, state, elementType); } @Override protected List<T> createPrivateData(boolean mutable) { if (mutable) { return Lists.newArrayList(); } return Collections.emptyList(); } @Override public void add(int index, T element) { validateElementType(element); getDelegate(true).add(index, element); } @Override public boolean addAll(int index, Collection<? extends T> c) { validateCollection(c); return getDelegate(true).addAll(index, c); } @Override public T get(int index) { return getDelegate(false).get(index); } @Override public int indexOf(Object o) { return getDelegate(false).indexOf(o); } @Override public int lastIndexOf(Object o) { return getDelegate(false).lastIndexOf(o); } @Override public ListIterator<T> listIterator() { return getDelegate(false).listIterator(); } @Override public ListIterator<T> listIterator(int index) { return getDelegate(false).listIterator(index); } @Override public T remove(int index) { return getDelegate(true).remove(index); } @Override public List<T> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); } @Override public T set(int index, T element) { validateElementType(element); return getDelegate(true).set(index, element); } } private static class SetBackedCollection<T> extends NodeBackedCollection<T, Set<T>> implements Set<T> { public SetBackedCollection( MutableModelNode modelNode, ModelViewState state, ModelType<T> elementType) { super(modelNode, state, elementType); } @Override protected Set<T> createPrivateData(boolean mutable) { if (mutable) { return Sets.newLinkedHashSet(); } else { return Collections.emptySet(); } } } }
@Override public ModelType<?> getManagedType() { return ModelType.of(this.getClass()); }
@Override public int hashCode() { return elementType.hashCode(); }
/** * A type token, representing a resolved type. * * <p>Importantly, instances do not hold strong references to class objects. * * <p>Construct a type via one of the public static methods, or by creating an AIC… * * <pre>{@code * ModelType<List<String>> type = new ModelType<List<String>>() {}; * }</pre> */ @ThreadSafe public abstract class ModelType<T> { public static final ModelType<Object> UNTYPED = ModelType.of(Object.class); private final TypeWrapper wrapper; private ModelType(TypeWrapper wrapper) { this.wrapper = wrapper; } protected ModelType() { this.wrapper = wrap(new TypeToken<T>(getClass()) {}.getType()); } public static <T> ModelType<T> of(Class<T> clazz) { return new Simple<T>(clazz); } public static <T> ModelType<T> returnType(Method method) { return new Simple<T>(method.getGenericReturnType()); } public static <T> ModelType<T> declaringType(Method method) { return new Simple<T>(method.getDeclaringClass()); } @Nullable public static <T> ModelType<T> paramType(Method method, int i) { Type[] parameterTypes = method.getGenericParameterTypes(); if (i < parameterTypes.length) { return new Simple<T>(parameterTypes[i]); } else { return null; } } public static <T> ModelType<T> typeOf(T instance) { // TODO: should validate that clazz is of a non parameterized type @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) instance.getClass(); return of(clazz); } public static ModelType<?> of(Type type) { return Simple.typed(type); } /** Returns true if this type represents a class. */ public boolean isClass() { return wrapper instanceof ClassTypeWrapper; } public Class<? super T> getRawClass() { return Cast.uncheckedCast(wrapper.getRawClass()); } public Class<T> getConcreteClass() { return Cast.uncheckedCast(wrapper.getRawClass()); } public boolean isRawClassOfParameterizedType() { return wrapper instanceof ClassTypeWrapper && ((ClassTypeWrapper) wrapper).unwrap().getTypeParameters().length > 0; } public static ModelType<Object> untyped() { return UNTYPED; } public boolean isParameterized() { return wrapper instanceof ParameterizedTypeWrapper; } public List<ModelType<?>> getTypeVariables() { if (isParameterized()) { TypeWrapper[] typeArguments = ((ParameterizedTypeWrapper) wrapper).getActualTypeArguments(); ImmutableList.Builder<ModelType<?>> builder = ImmutableList.builder(); for (TypeWrapper typeArgument : typeArguments) { builder.add(Simple.typed(typeArgument)); } return builder.build(); } else { return Collections.emptyList(); } } /** * Casts this {@code ModelType} object to represent a subclass of the class represented by the * specified class object. Checks that the cast is valid, and throws a {@code ClassCastException} * if it is not. If this method succeeds, it always returns a reference to this {@code ModelType} * object. * * @throws ClassCastException if this cannot be cast as the subtype of the given type. * @throws IllegalStateException if this is a wildcard. * @throws IllegalArgumentException if the given type is a wildcard. */ public <U> ModelType<? extends U> asSubtype(ModelType<U> modelType) { if (isWildcard()) { throw new IllegalStateException(this + " is a wildcard type"); } if (modelType.isWildcard()) { throw new IllegalArgumentException(modelType + " is a wildcard type"); } if (modelType.getRawClass().isAssignableFrom(getRawClass())) { return Cast.uncheckedCast(this); } else { throw new ClassCastException( String.format("'%s' cannot be cast as a subtype of '%s'", this, modelType)); } } public boolean isAssignableFrom(ModelType<?> modelType) { return modelType == this || wrapper.isAssignableFrom(modelType.wrapper); } public boolean isAnnotationPresent(Class<? extends Annotation> annotation) { return getRawClass().isAnnotationPresent(annotation); } public boolean isWildcard() { return getWildcardType() != null; } @Nullable public ModelType<?> getUpperBound() { WildcardWrapper wildcardType = getWildcardType(); if (wildcardType == null) { return null; } else { ModelType<?> upperBound = Simple.typed(wildcardType.getUpperBound()); if (upperBound.equals(UNTYPED)) { return null; } return upperBound; } } @Nullable public ModelType<?> getLowerBound() { WildcardWrapper wildcardType = getWildcardType(); if (wildcardType == null) { return null; } else { TypeWrapper lowerBound = wildcardType.getLowerBound(); if (lowerBound == null) { return null; } return Simple.typed(lowerBound); } } private WildcardWrapper getWildcardType() { if (wrapper instanceof WildcardWrapper) { return (WildcardWrapper) wrapper; } return null; } public boolean isHasWildcardTypeVariables() { if (isWildcard()) { return true; } else if (isParameterized()) { for (ModelType<?> typeVariable : getTypeVariables()) { if (typeVariable.isHasWildcardTypeVariables()) { return true; } } } return false; } public List<Class<?>> getAllClasses() { ImmutableList.Builder<Class<?>> builder = ImmutableList.builder(); wrapper.collectClasses(builder); return builder.build(); } public String getName() { return wrapper.getRepresentation(true); } /** Returns a human-readable name for the type. */ public String getDisplayName() { return wrapper.getRepresentation(false); } public String toString() { return wrapper.getRepresentation(true); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ModelType)) { return false; } ModelType<?> modelType = (ModelType<?>) o; return wrapper.equals(modelType.wrapper); } @Override public int hashCode() { return wrapper.hashCode(); } public abstract static class Builder<T> { private ParameterizedTypeWrapper wrapper; public Builder() { wrapper = (ParameterizedTypeWrapper) wrap(new TypeToken<T>(getClass()) {}.getType()); } @SuppressWarnings("unchecked") public <I> Builder<T> where(Parameter<I> parameter, ModelType<I> type) { wrapper = wrapper.substitute(parameter.typeVariable, type.wrapper); return this; } public ModelType<T> build() { return Simple.typed((TypeWrapper) wrapper); } } @SuppressWarnings("UnusedDeclaration") public abstract static class Parameter<T> { private final TypeVariable<?> typeVariable; public Parameter() { Type type = new TypeToken<T>(getClass()) {}.getType(); if (type instanceof TypeVariable<?>) { this.typeVariable = (TypeVariable<?>) type; } else { throw new IllegalStateException("T for Parameter<T> MUST be a type variable"); } } } private static final TypeWrapper[] EMPTY_TYPE_WRAPPER_ARRAY = new TypeWrapper[0]; @Nullable private static TypeWrapper wrap(Type type) { if (type == null) { return null; } else if (type instanceof Class) { return new ClassTypeWrapper((Class<?>) type); } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; return new ParameterizedTypeWrapper( toWrappers(parameterizedType.getActualTypeArguments()), (ClassTypeWrapper) wrap(parameterizedType.getRawType()), wrap(parameterizedType.getOwnerType())); } else if (type instanceof WildcardType) { WildcardType wildcardType = (WildcardType) type; return new WildcardTypeWrapper( toWrappers(wildcardType.getUpperBounds()), toWrappers(wildcardType.getLowerBounds()), type.hashCode()); } else if (type instanceof TypeVariable) { TypeVariable<?> typeVariable = (TypeVariable<?>) type; return new TypeVariableTypeWrapper( typeVariable.getName(), toWrappers(typeVariable.getBounds()), type.hashCode()); } else if (type instanceof GenericArrayType) { GenericArrayType genericArrayType = (GenericArrayType) type; return new GenericArrayTypeWrapper( wrap(genericArrayType.getGenericComponentType()), type.hashCode()); } else { throw new IllegalArgumentException("cannot wrap type of type " + type.getClass()); } } static TypeWrapper[] toWrappers(Type[] types) { if (types.length == 0) { return EMPTY_TYPE_WRAPPER_ARRAY; } else { TypeWrapper[] wrappers = new TypeWrapper[types.length]; int i = 0; for (Type type : types) { wrappers[i++] = wrap(type); } return wrappers; } } private static class Simple<T> extends ModelType<T> { public static <T> ModelType<T> typed(Type type) { return new Simple<T>(type); } public static <T> ModelType<T> typed(TypeWrapper wrapper) { return new Simple<T>(wrapper); } public Simple(Type type) { super(wrap(type)); } public Simple(TypeWrapper type) { super(type); } } }
/** * Generates an implementation of the given managed type. * * <p>The generated class will implement/extend the managed type and will: * * <ul> * <li>provide implementations for abstract getters and setters that delegate to model nodes * <li>provide a `toString()` implementation * <li>mix-in implementation of {@link ManagedInstance} * <li>provide a constructor that accepts a {@link ModelElementState}, which will be used to * implement the above. * </ul> * * In case a delegate schema is supplied, the generated class will also have: * * <ul> * <li>a constructor that also takes a delegate instance * <li>methods that call through to the delegate instance * </ul> */ public <T, M extends T, D extends T> Class<? extends M> generate( StructSchema<M> viewSchema, @Nullable StructSchema<D> delegateSchema) { if (delegateSchema != null && Modifier.isAbstract(delegateSchema.getType().getConcreteClass().getModifiers())) { throw new IllegalArgumentException("Delegate type must be null or a non-abstract type"); } ClassWriter visitor = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); ModelType<M> viewType = viewSchema.getType(); StringBuilder generatedTypeNameBuilder = new StringBuilder(viewType.getName()); if (delegateSchema != null) { generatedTypeNameBuilder .append("$BackedBy_") .append(delegateSchema.getType().getName().replaceAll("\\.", "_")); } else { generatedTypeNameBuilder.append("$Impl"); } String generatedTypeName = generatedTypeNameBuilder.toString(); Type generatedType = Type.getType("L" + generatedTypeName.replaceAll("\\.", "/") + ";"); Class<M> viewClass = viewType.getConcreteClass(); Class<?> superclass; final ImmutableSet.Builder<String> interfacesToImplement = ImmutableSet.builder(); final ImmutableSet.Builder<Class<?>> typesToDelegate = ImmutableSet.builder(); typesToDelegate.add(viewClass); interfacesToImplement.add(MANAGED_INSTANCE_TYPE); if (viewClass.isInterface()) { superclass = Object.class; interfacesToImplement.add(Type.getInternalName(viewClass)); } else { superclass = viewClass; } // TODO:LPTR This should be removed once BinaryContainer is a ModelMap // We need to also implement all the interfaces of the delegate type because otherwise // BinaryContainer won't recognize managed binaries as BinarySpecInternal if (delegateSchema != null) { ModelSchemaUtils.walkTypeHierarchy( delegateSchema.getType().getConcreteClass(), new ModelSchemaUtils.TypeVisitor<D>() { @Override public void visitType(Class<? super D> type) { if (type.isInterface()) { typesToDelegate.add(type); interfacesToImplement.add(Type.getInternalName(type)); } } }); } generateProxyClass( visitor, viewSchema, delegateSchema, interfacesToImplement.build(), typesToDelegate.build(), generatedType, Type.getType(superclass)); ClassLoader targetClassLoader = viewClass.getClassLoader(); if (delegateSchema != null) { // TODO - remove this once the above is removed try { viewClass.getClassLoader().loadClass(delegateSchema.getType().getConcreteClass().getName()); } catch (ClassNotFoundException e) { // Delegate class is not visible to managed view type -> view type is more general than // delegate type, so use the delegate classloader instead targetClassLoader = delegateSchema.getType().getConcreteClass().getClassLoader(); } } return defineClass(visitor, targetClassLoader, generatedTypeName); }
private static boolean isManagedCollection(ModelType<?> type) { Class<?> concreteClass = type.getConcreteClass(); return concreteClass.equals(ModelMap.class) || concreteClass.equals(ModelSet.class); }