use of io.micronaut.inject.ast.ParameterElement 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.ParameterElement in project micronaut-core by micronaut-projects.
the class WebSocketVisitor method visitMethod.
@Override
public void visitMethod(MethodElement element, VisitorContext context) {
if (skipValidation) {
return;
}
String uri = element.stringValue(WEB_SOCKET_COMPONENT).orElse("/ws");
UriMatchTemplate template = uriCache.computeIfAbsent(uri, UriMatchTemplate::of);
List<String> variables = template.getVariableNames();
ParameterElement[] parameters = element.getParameters();
if (ArrayUtils.isNotEmpty(parameters)) {
if (element.hasAnnotation(ON_OPEN)) {
for (ParameterElement parameter : parameters) {
if (isInvalidParameter(variables, parameter, WEB_SOCKET_SESSION, HTTP_REQUEST)) {
context.fail("Parameter to @OnOpen must either be a URI variable, a WebSocketSession , the HttpRequest, or annotated with an HTTP binding annotation (such as @Header)", parameter);
break;
}
}
} else if (element.hasAnnotation(ON_CLOSE)) {
for (ParameterElement parameter : parameters) {
if (isInvalidParameter(variables, parameter, WEB_SOCKET_SESSION, CLOSE_REASON)) {
context.fail("Parameter to @OnClose must either be a URI variable, a CloseReason, a WebSocketSession or annotated with an HTTP binding annotation (such as @Header)", parameter);
break;
}
}
} else if (element.hasAnnotation(ON_ERROR)) {
for (ParameterElement parameter : parameters) {
if (isInvalidParameter(variables, parameter, WEB_SOCKET_SESSION, Throwable.class.getName())) {
context.fail("Parameter to @OnError must either be a URI variable, a Throwable, a WebSocketSession or annotated with an HTTP binding annotation (such as @Header)", parameter);
break;
}
}
} else if (element.hasAnnotation(ON_MESSAGE)) {
List<ParameterElement> bodyParameters = new ArrayList<>(3);
for (ParameterElement parameter : parameters) {
if (isInvalidParameter(variables, parameter, WEB_SOCKET_SESSION)) {
// potential body parameter
bodyParameters.add(parameter);
}
}
if (bodyParameters.size() > 1) {
context.fail("WebSocket @OnMessage handler has multiple possible message body arguments. : " + bodyParameters, element);
}
}
}
}
use of io.micronaut.inject.ast.ParameterElement 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.ParameterElement 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.ParameterElement in project micronaut-core by micronaut-projects.
the class BeanDefinitionWriter method createParameterTypeArray.
private int createParameterTypeArray(List<ParameterElement> parameters, GeneratorAdapter buildMethodVisitor) {
final int pLen = parameters.size();
pushNewArray(buildMethodVisitor, Class.class, pLen);
for (int i = 0; i < pLen; i++) {
final ParameterElement parameter = parameters.get(i);
pushStoreInArray(buildMethodVisitor, i, pLen, () -> buildMethodVisitor.push(getTypeReference(parameter)));
}
int local = buildMethodVisitor.newLocal(Type.getType(Object[].class));
buildMethodVisitor.storeLocal(local);
return local;
}
Aggregations