org.kie.workbench.common.forms.adf.processors.FormDefinitionGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.kie.workbench.common.forms.adf.processors.FormDefinitionGenerator.java

Source

/*
 * Copyright 2019 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.kie.workbench.common.forms.adf.processors;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.Messager;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

import org.apache.commons.lang3.StringUtils;
import org.kie.workbench.common.forms.adf.definitions.annotations.FieldParam;
import org.kie.workbench.common.forms.adf.definitions.annotations.FormDefinition;
import org.kie.workbench.common.forms.adf.definitions.annotations.FormField;
import org.kie.workbench.common.forms.adf.definitions.annotations.SkipFormField;
import org.kie.workbench.common.forms.adf.definitions.annotations.field.selector.SelectorDataProvider;
import org.kie.workbench.common.forms.adf.definitions.annotations.i18n.I18nSettings;
import org.kie.workbench.common.forms.adf.definitions.annotations.layout.Column;
import org.kie.workbench.common.forms.adf.definitions.annotations.metaModel.FieldDefinition;
import org.kie.workbench.common.forms.adf.definitions.annotations.metaModel.FieldHelp;
import org.kie.workbench.common.forms.adf.definitions.annotations.metaModel.FieldLabel;
import org.kie.workbench.common.forms.adf.definitions.annotations.metaModel.FieldValue;
import org.kie.workbench.common.forms.adf.definitions.annotations.metaModel.I18nMode;
import org.kie.workbench.common.forms.adf.definitions.settings.ColSpan;
import org.kie.workbench.common.forms.adf.definitions.settings.FieldPolicy;
import org.kie.workbench.common.forms.adf.processors.util.FieldInfo;
import org.kie.workbench.common.forms.adf.processors.util.FormGenerationUtils;
import org.kie.workbench.common.forms.model.util.formModel.FormModelPropertiesUtil;
import org.uberfire.annotations.processors.GeneratorUtils;

import static org.kie.workbench.common.forms.adf.processors.FormDefinitionsProcessor.FORM_DEFINITON_ANNOTATION;

public class FormDefinitionGenerator {

    private static final String FORM_BUILDER_SUFFIX = "FormBuilder";

    private final SourceGenerationContext context;

    private TypeMirror listType;
    private TypeMirror enumType;

    public FormDefinitionGenerator(SourceGenerationContext context) {
        this.context = context;
    }

    public void generate() throws Exception {
        TypeElement annotation = context.getElementUtils().getTypeElement(FORM_DEFINITON_ANNOTATION);

        Set<? extends Element> formDefinitions = context.getRoundEnvironment().getElementsAnnotatedWith(annotation);

        context.getMessager().printMessage(Diagnostic.Kind.NOTE,
                "FormDefinitions found:  " + formDefinitions.size());

        // Initializing list type to avoid getting the element each time.
        listType = context.getElementUtils().getTypeElement(List.class.getName()).asType();
        enumType = context.getElementUtils().getTypeElement(Enum.class.getName()).asType();

        for (Element element : formDefinitions) {
            if (element.getKind().equals(ElementKind.CLASS)) {
                processFormDefinition((TypeElement) element);
            }
        }
    }

    protected void processFormDefinition(TypeElement formElement) throws Exception {
        final Messager messager = context.getMessager();

        messager.printMessage(Diagnostic.Kind.NOTE,
                "Discovered FormDefintion class [" + formElement.getSimpleName() + "]");

        boolean checkInheritance = false;

        FormDefinition definition = formElement.getAnnotation(FormDefinition.class);

        String modelClassName = formElement.getQualifiedName().toString();
        String builderClassName = FormGenerationUtils.fixClassName(formElement.getQualifiedName().toString())
                + FORM_BUILDER_SUFFIX;

        FormDefinitionData form = new FormDefinitionData(modelClassName, builderClassName);

        form.setStartElement(definition.startElement());
        form.setI18nBundle(StringUtils.isEmpty(definition.i18n().bundle()) ? formElement.asType().toString()
                : definition.i18n().bundle());

        Column[] columns = definition.layout().value();

        List<String> layoutColumns = new ArrayList<>();
        if (columns.length == 0) {
            layoutColumns.add(ColSpan.SPAN_12.getName());
        } else {
            for (Column column : columns) {
                layoutColumns.add(column.value().getName());
            }
        }

        form.setLayoutColumns(layoutColumns);

        checkInheritance = definition.allowInheritance();

        Map<String, String> defaultFieldSettings = new HashMap<>();

        for (FieldParam param : definition.defaultFieldSettings()) {
            defaultFieldSettings.put(param.name(), param.value());
        }

        List<FormDefinitionFieldData> formElements = new ArrayList<>();

        if (checkInheritance) {
            TypeElement parent = getParent(formElement);
            formElements.addAll(
                    extractParentFormFields(parent, definition.policy(), definition.i18n(), defaultFieldSettings));
        }

        formElements.addAll(
                extracFormFields(formElement, definition.policy(), definition.i18n(), defaultFieldSettings));

        FormGenerationUtils.sort(definition.startElement(), formElements);

        messager.printMessage(Diagnostic.Kind.NOTE, "Discovered " + formElements.size() + " elements for form ["
                + formElement.getQualifiedName().toString() + "]");

        form.getElements().addAll(formElements);

        context.getForms().add(form);
    }

    private List<FormDefinitionFieldData> extractParentFormFields(TypeElement element, FieldPolicy policy,
            I18nSettings i18nSettings, Map<String, String> defaultParams) throws Exception {

        if (element.toString().equals(Object.class.getName())) {
            return new ArrayList<>();
        }

        TypeElement parentElement = getParent(element);

        List<FormDefinitionFieldData> result = extractParentFormFields(parentElement, policy, i18nSettings,
                defaultParams);

        result.addAll(extracFormFields(element, policy, i18nSettings, defaultParams));

        return result;
    }

    private boolean filter(VariableElement fieldElement, FieldPolicy policy) {
        if (policy.equals(FieldPolicy.ALL)) {
            AnnotationMirror annotation = GeneratorUtils.getAnnotation(context.getElementUtils(), fieldElement,
                    SkipFormField.class.getName());
            if (annotation != null) {
                return false;
            }
        } else {
            AnnotationMirror annotation = GeneratorUtils.getAnnotation(context.getElementUtils(), fieldElement,
                    FormField.class.getName());
            if (annotation == null) {
                return false;
            }
        }
        return true;
    }

    private List<FormDefinitionFieldData> extracFormFields(TypeElement type, FieldPolicy policy,
            I18nSettings i18nSettings, Map<String, String> defaultParams) throws Exception {

        final Types typeUtils = context.getProcessingEnvironment().getTypeUtils();

        Collection<FieldInfo> fieldInfos = FormGenerationUtils.extractFieldInfos(type,
                fieldElement -> filter(fieldElement, policy));

        List<FormDefinitionFieldData> fieldSettings = new ArrayList<>();

        for (FieldInfo fieldInfo : fieldInfos) {

            if (fieldInfo.getSetter() != null && fieldInfo.getGetter() != null) {

                String fieldName = fieldInfo.getFieldElement().getSimpleName().toString();

                FormDefinitionFieldData fieldData = new FormDefinitionFieldData(type.getQualifiedName().toString(),
                        fieldName);

                fieldData.setLabel(fieldName);
                fieldData.setBinding(fieldName);
                fieldData.setMethodName("getFormElement_" + fieldName);

                boolean isList = false;

                org.kie.workbench.common.forms.model.TypeKind typeKind = org.kie.workbench.common.forms.model.TypeKind.BASE;

                boolean overrideI18n = false;

                TypeMirror finalType = fieldInfo.getFieldElement().asType();
                TypeElement finalTypeElement = (TypeElement) typeUtils.asElement(finalType);

                String fieldModifier = "";

                if (finalTypeElement.getKind().equals(ElementKind.CLASS)) {
                    FieldDefinition fieldDefinitionAnnotation = finalTypeElement
                            .getAnnotation(FieldDefinition.class);
                    if (fieldDefinitionAnnotation != null) {

                        // Override the using the i18n mechanism
                        if (fieldDefinitionAnnotation.i18nMode().equals(I18nMode.OVERRIDE_I18N_KEY)) {
                            fieldData.setLabel(finalType.toString() + i18nSettings.separator()
                                    + fieldDefinitionAnnotation.labelKeySuffix());
                            Collection<FieldInfo> labelInfos = FormGenerationUtils.extractFieldInfos(
                                    finalTypeElement,
                                    fieldElement -> fieldElement.getAnnotation(FieldLabel.class) != null);

                            if (labelInfos != null && labelInfos.size() == 1) {
                                FieldInfo labelInfo = labelInfos.iterator().next();
                                fieldData.setLabel(finalType.toString() + i18nSettings.separator()
                                        + labelInfo.getFieldElement().getSimpleName());
                            }

                            fieldData.setHelpMessage(finalType.toString() + i18nSettings.separator()
                                    + fieldDefinitionAnnotation.helpMessageKeySuffix());
                            Collection<FieldInfo> helpMessages = FormGenerationUtils.extractFieldInfos(
                                    finalTypeElement,
                                    fieldElement -> fieldElement.getAnnotation(FieldHelp.class) != null);

                            if (helpMessages != null && helpMessages.size() == 1) {
                                FieldInfo helpInfo = helpMessages.iterator().next();
                                fieldData.setHelpMessage(finalType.toString() + i18nSettings.separator()
                                        + helpInfo.getFieldElement().getSimpleName());
                            }
                        }

                        Collection<FieldInfo> fieldValue = FormGenerationUtils.extractFieldInfos(finalTypeElement,
                                fieldElement -> fieldElement.getAnnotation(FieldValue.class) != null);

                        if (fieldValue == null || fieldValue.size() != 1) {
                            throw new Exception("Problem processing FieldDefinition [" + finalType
                                    + "]: it should have one field marked as @FieldValue");
                        }
                        FieldInfo valueInfo = fieldValue.iterator().next();

                        fieldData.setBinding(
                                fieldData.getBinding() + "." + valueInfo.getFieldElement().getSimpleName());

                        fieldModifier = FormGenerationUtils.fixClassName(finalType.toString())
                                + "_FieldStatusModifier";

                        finalType = valueInfo.getFieldElement().asType();
                        finalTypeElement = (TypeElement) typeUtils.asElement(finalType);

                        overrideI18n = !fieldDefinitionAnnotation.i18nMode().equals(I18nMode.DONT_OVERRIDE);
                    } else {
                        FormDefinition formDefinitionAnnotation = finalTypeElement
                                .getAnnotation(FormDefinition.class);

                        if (formDefinitionAnnotation != null) {
                            fieldData.setLabel(
                                    finalType.toString() + i18nSettings.separator() + FieldDefinition.LABEL);
                            Collection<FieldInfo> labelInfos = FormGenerationUtils.extractFieldInfos(
                                    finalTypeElement,
                                    fieldElement -> fieldElement.getAnnotation(FieldLabel.class) != null);

                            if (labelInfos != null && labelInfos.size() == 1) {
                                FieldInfo labelInfo = labelInfos.iterator().next();
                                fieldData.setLabel(finalType.toString() + i18nSettings.separator()
                                        + labelInfo.getFieldElement().getSimpleName());
                                overrideI18n = true;
                            }

                            fieldData.setHelpMessage(
                                    finalType.toString() + i18nSettings.separator() + FieldDefinition.HELP_MESSAGE);
                            Collection<FieldInfo> helpMessages = FormGenerationUtils.extractFieldInfos(
                                    finalTypeElement,
                                    fieldElement -> fieldElement.getAnnotation(FieldHelp.class) != null);

                            if (helpMessages != null && helpMessages.size() == 1) {
                                FieldInfo helpInfo = helpMessages.iterator().next();
                                fieldData.setHelpMessage(finalType.toString() + i18nSettings.separator()
                                        + helpInfo.getFieldElement().getSimpleName());
                                overrideI18n = true;
                            }

                            typeKind = org.kie.workbench.common.forms.model.TypeKind.OBJECT;
                        }
                    }
                }

                DeclaredType fieldType = (DeclaredType) finalType;

                if (typeUtils.isAssignable(finalTypeElement.asType(), listType)) {
                    if (fieldType.getTypeArguments().size() != 1) {
                        throw new IllegalArgumentException("Impossible to generate a field for type "
                                + fieldType.toString() + ". Type should have one and only one Type arguments.");
                    }
                    isList = true;
                    finalType = fieldType.getTypeArguments().get(0);
                    finalTypeElement = (TypeElement) typeUtils.asElement(finalType);

                    if (FormModelPropertiesUtil.isBaseType(finalTypeElement.getQualifiedName().toString())) {
                        typeKind = org.kie.workbench.common.forms.model.TypeKind.BASE;
                    } else if (typeUtils.isAssignable(finalTypeElement.asType(), enumType)) {
                        typeKind = org.kie.workbench.common.forms.model.TypeKind.ENUM;
                    } else {
                        typeKind = org.kie.workbench.common.forms.model.TypeKind.OBJECT;
                    }
                } else if (typeUtils.isAssignable(finalTypeElement.asType(), enumType)) {
                    typeKind = org.kie.workbench.common.forms.model.TypeKind.ENUM;
                }

                fieldData.setType(typeKind.toString());
                fieldData.setClassName(finalTypeElement.getQualifiedName().toString());
                fieldData.setList(String.valueOf(isList));
                fieldData.setFieldModifier(fieldModifier);
                fieldData.getParams().putAll(defaultParams);

                FormField settings = fieldInfo.getFieldElement().getAnnotation(FormField.class);

                if (settings != null) {
                    try {
                        fieldData.setPreferredType(settings.type().getName());
                    } catch (MirroredTypeException exception) {
                        fieldData.setPreferredType(exception.getTypeMirror().toString());
                    }

                    fieldData.setAfterElement(settings.afterElement());

                    if (!overrideI18n && !isEmpty(settings.labelKey())) {
                        fieldData.setLabel(settings.labelKey());
                    }

                    if (!overrideI18n && !isEmpty(settings.helpMessageKey())) {
                        fieldData.setHelpMessage(settings.helpMessageKey());
                    }

                    fieldData.setRequired(Boolean.valueOf(settings.required()).toString());
                    fieldData.setReadOnly(Boolean.valueOf(settings.readonly()).toString());

                    for (FieldParam fieldParam : settings.settings()) {
                        fieldData.getParams().put(fieldParam.name(), fieldParam.value());
                    }

                    fieldData.setWrap(Boolean.valueOf(settings.layoutSettings().wrap()).toString());
                    fieldData.setHorizontalSpan(String.valueOf(settings.layoutSettings().horizontalSpan()));
                    fieldData.setVerticalSpan(String.valueOf(settings.layoutSettings().verticalSpan()));
                }

                if (!overrideI18n) {
                    if (!isEmpty(i18nSettings.keyPreffix())) {
                        fieldData.setLabel(
                                i18nSettings.keyPreffix() + i18nSettings.separator() + fieldData.getLabel());
                        if (!isEmpty(fieldData.getHelpMessage())) {
                            fieldData.setHelpMessage(i18nSettings.keyPreffix() + i18nSettings.separator()
                                    + fieldData.getHelpMessage());
                        }
                    }
                }

                extractFieldExtraSettings(fieldData, fieldInfo.getFieldElement());

                fieldSettings.add(fieldData);
            }
        }
        return fieldSettings;
    }

    protected void extractFieldExtraSettings(FormDefinitionFieldData fieldData, Element fieldElement) {
        SelectorDataProvider selectorDataProvider = fieldElement.getAnnotation(SelectorDataProvider.class);
        if (selectorDataProvider != null) {
            String provider = selectorDataProvider.type().getCode() + ":" + selectorDataProvider.className();
            fieldData.getParams().put(SelectorDataProvider.class.getName(), provider);
        }
    }

    private TypeElement getParent(TypeElement classElement) {
        return (TypeElement) context.getProcessingEnvironment().getTypeUtils()
                .asElement(classElement.getSuperclass());
    }

    private static final boolean isEmpty(final String s) {
        return StringUtils.isEmpty(s);
    }
}