use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.
the class NullableParameterRule method validate.
@Override
public RouteValidationResult validate(List<UriMatchTemplate> templates, ParameterElement[] parameters, MethodElement method) {
List<String> errorMessages = new ArrayList<>();
boolean isClient = method.hasAnnotation("io.micronaut.http.client.annotation.Client");
// Optional variables can be required in clients
if (!isClient) {
Map<String, UriMatchVariable> variables = new HashMap<>();
Set<UriMatchVariable> required = new HashSet<>();
for (UriMatchTemplate template : templates) {
for (UriMatchVariable variable : template.getVariables()) {
if (!variable.isOptional() || variable.isExploded()) {
required.add(variable);
}
variables.compute(variable.getName(), (key, var) -> {
if (var == null) {
if (variable.isOptional() && !variable.isExploded()) {
return variable;
} else {
return null;
}
} else {
if (!var.isOptional() || var.isExploded()) {
if (variable.isOptional() && !variable.isExploded()) {
return variable;
} else {
return var;
}
} else {
return var;
}
}
});
}
}
for (UriMatchVariable variable : required) {
if (templates.stream().anyMatch(t -> !t.getVariableNames().contains(variable.getName()))) {
variables.putIfAbsent(variable.getName(), variable);
}
}
for (UriMatchVariable variable : variables.values()) {
Arrays.stream(parameters).flatMap(p -> getTypedElements(p).stream()).filter(p -> p.getName().equals(variable.getName())).forEach(p -> {
ClassElement type = p.getType();
boolean hasDefaultValue = p.findAnnotation(Bindable.class).flatMap(av -> av.stringValue("defaultValue")).isPresent();
if (!isNullable(p) && type != null && !type.isAssignable(Optional.class) && !hasDefaultValue) {
errorMessages.add(String.format("The uri variable [%s] is optional, but the corresponding method argument [%s %s] is not defined as an Optional or annotated with a Nullable annotation.", variable.getName(), p.getType().toString(), p.getName()));
}
});
}
}
return new RouteValidationResult(errorMessages.toArray(new String[0]));
}
use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.
the class BeanIntrospectionWriter method writeInstantiateMethod.
private void writeInstantiateMethod(ClassWriter classWriter, MethodElement constructor, String methodName, Class... args) {
final String desc = getMethodDescriptor(Object.class, Arrays.asList(args));
final GeneratorAdapter instantiateInternal = new GeneratorAdapter(classWriter.visitMethod(ACC_PUBLIC, methodName, desc, null, null), ACC_PUBLIC, methodName, desc);
invokeBeanConstructor(instantiateInternal, constructor, (writer, con) -> {
List<ParameterElement> constructorArguments = Arrays.asList(con.getParameters());
Collection<Type> argumentTypes = constructorArguments.stream().map(pe -> JavaModelUtils.getTypeReference(pe.getType())).collect(Collectors.toList());
int i = 0;
for (Type argumentType : argumentTypes) {
writer.loadArg(0);
writer.push(i++);
writer.arrayLoad(TYPE_OBJECT);
pushCastToType(writer, argumentType);
}
});
instantiateInternal.returnValue();
instantiateInternal.visitMaxs(3, 1);
instantiateInternal.visitEnd();
}
use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.
the class BeanIntrospectionWriter method visitProperty.
/**
* Visit a property.
*
* @param type The property type
* @param genericType The generic type
* @param name The property name
* @param readMethod The read method
* @param writeMethod The write methodname
* @param isReadOnly Is the property read only
* @param annotationMetadata The property annotation metadata
* @param typeArguments The type arguments
*/
void visitProperty(@NonNull TypedElement type, @NonNull TypedElement genericType, @NonNull String name, @Nullable MethodElement readMethod, @Nullable MethodElement writeMethod, boolean isReadOnly, @Nullable AnnotationMetadata annotationMetadata, @Nullable Map<String, ClassElement> typeArguments) {
DefaultAnnotationMetadata.contributeDefaults(this.annotationMetadata, annotationMetadata);
if (typeArguments != null) {
for (ClassElement element : typeArguments.values()) {
DefaultAnnotationMetadata.contributeRepeatable(this.annotationMetadata, element);
}
}
int readMethodIndex = -1;
if (readMethod != null) {
readMethodIndex = dispatchWriter.addMethod(classElement, readMethod, true);
}
int writeMethodIndex = -1;
int withMethodIndex = -1;
if (writeMethod != null) {
writeMethodIndex = dispatchWriter.addMethod(classElement, writeMethod, true);
}
boolean isMutable = !isReadOnly || hasAssociatedConstructorArgument(name, genericType);
if (isMutable) {
if (writeMethod == null) {
final String prefix = this.annotationMetadata.stringValue(Introspected.class, "withPrefix").orElse("with");
ElementQuery<MethodElement> elementQuery = ElementQuery.of(MethodElement.class).onlyAccessible().onlyDeclared().onlyInstance().named((n) -> n.startsWith(prefix) && n.equals(prefix + NameUtils.capitalize(name))).filter((methodElement -> {
ParameterElement[] parameters = methodElement.getParameters();
return parameters.length == 1 && methodElement.getGenericReturnType().getName().equals(classElement.getName()) && type.getType().isAssignable(parameters[0].getType());
}));
MethodElement withMethod = classElement.getEnclosedElement(elementQuery).orElse(null);
if (withMethod != null) {
withMethodIndex = dispatchWriter.addMethod(classElement, withMethod, true);
} else {
MethodElement constructor = this.constructor == null ? defaultConstructor : this.constructor;
if (constructor != null) {
withMethodIndex = dispatchWriter.addDispatchTarget(new CopyConstructorDispatchTarget(constructor, name));
}
}
}
// Otherwise, set method would be used in BeanProperty
} else {
withMethodIndex = dispatchWriter.addDispatchTarget(new ExceptionDispatchTarget(UnsupportedOperationException.class, "Cannot mutate property [" + name + "] that is not mutable via a setter method or constructor argument for type: " + beanType.getClassName()));
}
beanProperties.add(new BeanPropertyData(genericType, name, annotationMetadata, typeArguments, readMethodIndex, writeMethodIndex, withMethodIndex, isReadOnly));
}
use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.
the class BeanDefinitionWriter method visitDefaultConstructor.
@Override
public void visitDefaultConstructor(AnnotationMetadata annotationMetadata, VisitorContext visitorContext) {
if (this.constructor == null) {
ClassElement bean = ClassElement.of(beanType.getClassName());
MethodElement defaultConstructor = MethodElement.of(bean, annotationMetadata, bean, bean, "<init>");
constructor = defaultConstructor;
// now prepare the implementation of the build method. See BeanFactory interface
visitBuildMethodDefinition(defaultConstructor, false);
// now override the injectBean method
visitInjectMethodDefinition();
}
}
use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.
the class BeanDefinitionWriter method visitBuildFactoryMethodDefinition.
private void visitBuildFactoryMethodDefinition(ClassElement factoryClass, Element factoryMethod) {
if (buildMethodVisitor == null) {
ParameterElement[] parameters;
if (factoryMethod instanceof MethodElement) {
parameters = ((MethodElement) factoryMethod).getParameters();
} else {
parameters = new ParameterElement[0];
}
List<ParameterElement> parameterList = Arrays.asList(parameters);
boolean isParametrized = isParametrized(parameters);
boolean isIntercepted = isConstructorIntercepted(factoryMethod);
Type factoryType = JavaModelUtils.getTypeReference(factoryClass);
defineBuilderMethod(isParametrized);
// load this
GeneratorAdapter buildMethodVisitor = this.buildMethodVisitor;
// for Factory beans first we need to lookup the the factory bean
// before invoking the method to instantiate
// the below code looks up the factory bean.
// Load the BeanContext for the method call
buildMethodVisitor.loadArg(1);
pushCastToType(buildMethodVisitor, DefaultBeanContext.class);
// load the first argument of the method (the BeanResolutionContext) to be passed to the method
buildMethodVisitor.loadArg(0);
// second argument is the bean type
buildMethodVisitor.push(factoryType);
buildMethodVisitor.invokeVirtual(Type.getType(DefaultBeanContext.class), org.objectweb.asm.commons.Method.getMethod(METHOD_GET_BEAN));
// store a reference to the bean being built at index 3
int factoryVar = buildMethodVisitor.newLocal(JavaModelUtils.getTypeReference(factoryClass));
buildMethodVisitor.storeLocal(factoryVar);
buildMethodVisitor.loadLocal(factoryVar);
pushCastToType(buildMethodVisitor, factoryClass);
String methodDescriptor = getMethodDescriptorForReturnType(beanType, parameterList);
boolean hasInjectScope = false;
if (isIntercepted) {
int constructorIndex = initInterceptedConstructorWriter(buildMethodVisitor, parameterList, new FactoryMethodDef(factoryType, factoryMethod, methodDescriptor, factoryVar));
// populate an Object[] of all constructor arguments
final int parametersIndex = createParameterArray(parameterList, buildMethodVisitor);
invokeConstructorChain(buildMethodVisitor, constructorIndex, parametersIndex, parameterList);
} else {
if (!parameterList.isEmpty()) {
hasInjectScope = pushConstructorArguments(buildMethodVisitor, parameters);
}
if (factoryMethod instanceof MethodElement) {
buildMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, factoryType.getInternalName(), factoryMethod.getName(), methodDescriptor, false);
} else {
buildMethodVisitor.getField(factoryType, factoryMethod.getName(), beanType);
}
}
this.buildInstanceLocalVarIndex = buildMethodVisitor.newLocal(beanType);
buildMethodVisitor.storeLocal(buildInstanceLocalVarIndex);
if (!isPrimitiveBean) {
pushBeanDefinitionMethodInvocation(buildMethodVisitor, "injectBean");
pushCastToType(buildMethodVisitor, beanType);
buildMethodVisitor.storeLocal(buildInstanceLocalVarIndex);
}
destroyInjectScopeBeansIfNecessary(buildMethodVisitor, hasInjectScope);
buildMethodVisitor.loadLocal(buildInstanceLocalVarIndex);
initLifeCycleMethodsIfNecessary();
}
}
Aggregations