Search in sources :

Example 21 with ParameterElement

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]));
}
Also used : Bindable(io.micronaut.core.bind.annotation.Bindable) Arrays(java.util.Arrays) RouteValidationResult(io.micronaut.validation.routes.RouteValidationResult) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) Set(java.util.Set) UriMatchTemplate(io.micronaut.http.uri.UriMatchTemplate) ClassElement(io.micronaut.inject.ast.ClassElement) HashMap(java.util.HashMap) Collectors(java.util.stream.Collectors) ParameterElement(io.micronaut.inject.ast.ParameterElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) List(java.util.List) TypedElement(io.micronaut.inject.ast.TypedElement) Map(java.util.Map) MethodElement(io.micronaut.inject.ast.MethodElement) Optional(java.util.Optional) NameUtils(io.micronaut.core.naming.NameUtils) Collections(java.util.Collections) Optional(java.util.Optional) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) ClassElement(io.micronaut.inject.ast.ClassElement) UriMatchTemplate(io.micronaut.http.uri.UriMatchTemplate) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) RouteValidationResult(io.micronaut.validation.routes.RouteValidationResult) HashSet(java.util.HashSet)

Example 22 with ParameterElement

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);
            }
        }
    }
}
Also used : ArrayList(java.util.ArrayList) List(java.util.List) ParameterElement(io.micronaut.inject.ast.ParameterElement) UriMatchTemplate(io.micronaut.http.uri.UriMatchTemplate)

Example 23 with ParameterElement

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();
}
Also used : ElementQuery(io.micronaut.inject.ast.ElementQuery) Arrays(java.util.Arrays) ArrayUtils(io.micronaut.core.util.ArrayUtils) FieldElement(io.micronaut.inject.ast.FieldElement) Internal(io.micronaut.core.annotation.Internal) BeanIntrospectionReference(io.micronaut.core.beans.BeanIntrospectionReference) Type(org.objectweb.asm.Type) Map(java.util.Map) NameUtils(io.micronaut.core.naming.NameUtils) AbstractBeanIntrospectionReference(io.micronaut.core.beans.AbstractBeanIntrospectionReference) AbstractAnnotationMetadataWriter(io.micronaut.inject.writer.AbstractAnnotationMetadataWriter) AnnotationMetadataWriter(io.micronaut.inject.annotation.AnnotationMetadataWriter) AnnotationMetadataHierarchy(io.micronaut.inject.annotation.AnnotationMetadataHierarchy) Collection(java.util.Collection) Set(java.util.Set) NotNull(javax.validation.constraints.NotNull) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) Introspected(io.micronaut.core.annotation.Introspected) List(java.util.List) MethodElement(io.micronaut.inject.ast.MethodElement) JavaModelUtils(io.micronaut.inject.processing.JavaModelUtils) ClassWriter(org.objectweb.asm.ClassWriter) AbstractInitializableBeanIntrospection(io.micronaut.inject.beans.AbstractInitializableBeanIntrospection) Label(org.objectweb.asm.Label) ClassElement(io.micronaut.inject.ast.ClassElement) HashMap(java.util.HashMap) StringSwitchWriter(io.micronaut.inject.writer.StringSwitchWriter) DefaultAnnotationMetadata(io.micronaut.inject.annotation.DefaultAnnotationMetadata) ConstructorElement(io.micronaut.inject.ast.ConstructorElement) ParameterElement(io.micronaut.inject.ast.ParameterElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Method(org.objectweb.asm.commons.Method) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) TypedElement(io.micronaut.inject.ast.TypedElement) Nullable(io.micronaut.core.annotation.Nullable) AnnotationMetadataReference(io.micronaut.inject.annotation.AnnotationMetadataReference) BiConsumer(java.util.function.BiConsumer) Argument(io.micronaut.core.type.Argument) ClassWriterOutputVisitor(io.micronaut.inject.writer.ClassWriterOutputVisitor) LinkedHashSet(java.util.LinkedHashSet) OutputStream(java.io.OutputStream) Opcodes(org.objectweb.asm.Opcodes) IOException(java.io.IOException) ReflectionUtils(io.micronaut.core.reflect.ReflectionUtils) NonNull(io.micronaut.core.annotation.NonNull) DispatchWriter(io.micronaut.inject.writer.DispatchWriter) BeanIntrospection(io.micronaut.core.beans.BeanIntrospection) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Collections(java.util.Collections) Type(org.objectweb.asm.Type) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) ParameterElement(io.micronaut.inject.ast.ParameterElement)

Example 24 with ParameterElement

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));
}
Also used : ElementQuery(io.micronaut.inject.ast.ElementQuery) Arrays(java.util.Arrays) ArrayUtils(io.micronaut.core.util.ArrayUtils) FieldElement(io.micronaut.inject.ast.FieldElement) Internal(io.micronaut.core.annotation.Internal) BeanIntrospectionReference(io.micronaut.core.beans.BeanIntrospectionReference) Type(org.objectweb.asm.Type) Map(java.util.Map) NameUtils(io.micronaut.core.naming.NameUtils) AbstractBeanIntrospectionReference(io.micronaut.core.beans.AbstractBeanIntrospectionReference) AbstractAnnotationMetadataWriter(io.micronaut.inject.writer.AbstractAnnotationMetadataWriter) AnnotationMetadataWriter(io.micronaut.inject.annotation.AnnotationMetadataWriter) AnnotationMetadataHierarchy(io.micronaut.inject.annotation.AnnotationMetadataHierarchy) Collection(java.util.Collection) Set(java.util.Set) NotNull(javax.validation.constraints.NotNull) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) Introspected(io.micronaut.core.annotation.Introspected) List(java.util.List) MethodElement(io.micronaut.inject.ast.MethodElement) JavaModelUtils(io.micronaut.inject.processing.JavaModelUtils) ClassWriter(org.objectweb.asm.ClassWriter) AbstractInitializableBeanIntrospection(io.micronaut.inject.beans.AbstractInitializableBeanIntrospection) Label(org.objectweb.asm.Label) ClassElement(io.micronaut.inject.ast.ClassElement) HashMap(java.util.HashMap) StringSwitchWriter(io.micronaut.inject.writer.StringSwitchWriter) DefaultAnnotationMetadata(io.micronaut.inject.annotation.DefaultAnnotationMetadata) ConstructorElement(io.micronaut.inject.ast.ConstructorElement) ParameterElement(io.micronaut.inject.ast.ParameterElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Method(org.objectweb.asm.commons.Method) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) TypedElement(io.micronaut.inject.ast.TypedElement) Nullable(io.micronaut.core.annotation.Nullable) AnnotationMetadataReference(io.micronaut.inject.annotation.AnnotationMetadataReference) BiConsumer(java.util.function.BiConsumer) Argument(io.micronaut.core.type.Argument) ClassWriterOutputVisitor(io.micronaut.inject.writer.ClassWriterOutputVisitor) LinkedHashSet(java.util.LinkedHashSet) OutputStream(java.io.OutputStream) Opcodes(org.objectweb.asm.Opcodes) IOException(java.io.IOException) ReflectionUtils(io.micronaut.core.reflect.ReflectionUtils) NonNull(io.micronaut.core.annotation.NonNull) DispatchWriter(io.micronaut.inject.writer.DispatchWriter) BeanIntrospection(io.micronaut.core.beans.BeanIntrospection) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Collections(java.util.Collections) Introspected(io.micronaut.core.annotation.Introspected) MethodElement(io.micronaut.inject.ast.MethodElement) ClassElement(io.micronaut.inject.ast.ClassElement)

Example 25 with ParameterElement

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;
}
Also used : ParameterElement(io.micronaut.inject.ast.ParameterElement) AbstractConstructorInjectionPoint(io.micronaut.context.AbstractConstructorInjectionPoint) ConstructorInjectionPoint(io.micronaut.inject.ConstructorInjectionPoint)

Aggregations

ParameterElement (io.micronaut.inject.ast.ParameterElement)32 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)13 ClassElement (io.micronaut.inject.ast.ClassElement)13 Type (org.objectweb.asm.Type)13 GeneratorAdapter (org.objectweb.asm.commons.GeneratorAdapter)13 DefaultAnnotationMetadata (io.micronaut.inject.annotation.DefaultAnnotationMetadata)12 MethodElement (io.micronaut.inject.ast.MethodElement)9 ArrayList (java.util.ArrayList)8 List (java.util.List)8 AbstractConstructorInjectionPoint (io.micronaut.context.AbstractConstructorInjectionPoint)7 Label (org.objectweb.asm.Label)7 Argument (io.micronaut.core.type.Argument)6 ConstructorInjectionPoint (io.micronaut.inject.ConstructorInjectionPoint)6 MutableAnnotationMetadata (io.micronaut.inject.annotation.MutableAnnotationMetadata)6 TypedElement (io.micronaut.inject.ast.TypedElement)6 Map (java.util.Map)6 AnnotationMetadataReference (io.micronaut.inject.annotation.AnnotationMetadataReference)5 FieldElement (io.micronaut.inject.ast.FieldElement)5 HashMap (java.util.HashMap)5 ClassWriter (org.objectweb.asm.ClassWriter)5