/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.extension.internal.capability.xml.description;
import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage;
import static org.mule.runtime.module.extension.internal.util.IntrospectionUtils.getOperationMethods;
import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.runtime.api.meta.model.declaration.fluent.OperationDeclaration;
import org.mule.runtime.api.meta.model.declaration.fluent.WithOperationsDeclaration;
import org.mule.runtime.extension.api.annotation.Configurations;
import org.mule.runtime.extension.api.annotation.Operations;
import org.mule.runtime.module.extension.internal.capability.xml.schema.MethodDocumentation;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
/**
* {@link AbstractDescriptionDocumenter} implementation that fills {@link WithOperationsDeclaration}s
*
* @since 4.0
*/
final class OperationDescriptionDocumenter extends AbstractDescriptionDocumenter<WithOperationsDeclaration<?>> {
private final ParameterDescriptionDocumenter parameterDeclarer;
OperationDescriptionDocumenter(ProcessingEnvironment processingEnv) {
super(processingEnv);
this.parameterDeclarer = new ParameterDescriptionDocumenter(processingEnv);
}
void document(WithOperationsDeclaration<?> declaration, TypeElement element) {
final Map<String, Element> methods = getAllOperations(processingEnv, element);
try {
for (OperationDeclaration operation : declaration.getOperations()) {
Element method = methods.get(operation.getName());
// there are two cases in which method can be null:
// * A synthetic operation which was not defined in any class but added by a model property
// * An extension which operations are defined across multiple classes and the one being processed is not
// the one which defined the operation being processed
if (method == null) {
continue;
}
MethodDocumentation documentation = processor.getMethodDocumentation(processingEnv, method);
operation.setDescription(documentation.getSummary());
parameterDeclarer.document(operation, documentation);
}
} catch (Exception e) {
throw new MuleRuntimeException(createStaticMessage("Exception found while trying to obtain descriptions"), e);
}
}
private Map<String, Element> getAllOperations(ProcessingEnvironment processingEnv, Element element) {
Map<String, Element> elements = new LinkedHashMap<>();
Configurations configurations = processor.getAnnotationFromType(processingEnv, (TypeElement) element, Configurations.class);
if (configurations != null) {
List<TypeElement> configs = processor.getAnnotationClassesValue(element, Configurations.class, configurations.value());
configs.forEach(c -> elements.putAll(getOperationMethodElements(processingEnv, c)));
}
elements.putAll(getOperationMethodElements(processingEnv, element));
return elements;
}
private Map<String, Element> getOperationMethodElements(ProcessingEnvironment processingEnv, Element withOperationsElement) {
ImmutableMap.Builder<String, Element> methods = ImmutableMap.builder();
Operations operationsAnnotation =
processor.getAnnotationFromType(processingEnv, (TypeElement) withOperationsElement, Operations.class);
if (operationsAnnotation != null) {
final Class<?>[] operationsClasses = operationsAnnotation.value();
for (Class<?> operationClass : operationsClasses) {
while (operationClass != null && !operationClass.equals(Object.class)) {
TypeElement operationElement = processingEnv.getElementUtils().getTypeElement(operationClass.getName());
if (operationElement != null) {
for (Method operation : getOperationMethods(operationClass)) {
operationElement.getEnclosedElements().stream()
.filter(e -> e.getSimpleName().toString().equals(operation.getName())).findFirst()
.ifPresent(operationMethodElement -> methods.put(operation.getName(), operationMethodElement));
}
operationClass = operationClass.getSuperclass();
}
}
}
}
return methods.build();
}
}