com.haulmont.cuba.gui.xml.layout.loaders.FieldGroupLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.gui.xml.layout.loaders.FieldGroupLoader.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * 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 com.haulmont.cuba.gui.xml.layout.loaders;

import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.haulmont.bali.util.Dom4j;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.MessageTools;
import com.haulmont.cuba.core.global.MetadataTools;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.GuiDevelopmentException;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.FieldGroup.CustomFieldGenerator;
import com.haulmont.cuba.gui.components.FieldGroup.FieldCaptionAlignment;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributeCustomFieldGenerator;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.xml.DeclarativeFieldGenerator;
import com.haulmont.cuba.gui.xml.layout.ComponentLoader;
import com.haulmont.cuba.gui.xml.layout.LayoutLoader;
import com.haulmont.cuba.security.entity.EntityOp;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;

import static com.haulmont.bali.util.Preconditions.checkNotNullArgument;

public class FieldGroupLoader extends AbstractComponentLoader<FieldGroup> {

    protected DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
    protected MetadataTools metadataTools = AppBeans.get(MetadataTools.class);

    @Override
    public void createComponent() {
        resultComponent = (FieldGroup) factory.createComponent(FieldGroup.NAME);
        loadId(resultComponent, element);

        // required for border visible
        loadBorder(resultComponent, element);
    }

    @Override
    public void loadComponent() {
        assignFrame(resultComponent);

        String fieldFactoryBean = element.attributeValue("fieldFactoryBean");
        if (StringUtils.isNotEmpty(fieldFactoryBean)) {
            FieldGroupFieldFactory fieldFactory = AppBeans.get(fieldFactoryBean, FieldGroupFieldFactory.class);
            resultComponent.setFieldFactory(fieldFactory);
        }

        assignXmlDescriptor(resultComponent, element);

        loadVisible(resultComponent, element);
        loadWidth(resultComponent, element);

        loadEditable(resultComponent, element);
        loadEnable(resultComponent, element);

        loadStyleName(resultComponent, element);

        loadIcon(resultComponent, element);
        loadCaption(resultComponent, element);
        loadDescription(resultComponent, element);

        loadHeight(resultComponent, element);

        loadAlign(resultComponent, element);

        loadCaptionAlignment(resultComponent, element);

        loadFieldCaptionWidth(resultComponent, element);

        Datasource ds = loadDatasource(element);
        resultComponent.setDatasource(ds);

        if (element.elements("column").isEmpty()) {
            Iterable<FieldGroup.FieldConfig> rootFields = loadFields(resultComponent, element, ds, null);
            Iterable<FieldGroup.FieldConfig> dynamicAttributeFields = loadDynamicAttributeFields(ds);
            for (FieldGroup.FieldConfig field : dynamicAttributeFields) {
                if (resultComponent.getWidth() > 0 && field.getWidth() == null) {
                    field.setWidth("100%");
                }
            }
            for (FieldGroup.FieldConfig field : Iterables.concat(rootFields, dynamicAttributeFields)) {
                resultComponent.addField(field);
            }
        } else {
            @SuppressWarnings("unchecked")
            List<Element> columnElements = element.elements("column");
            @SuppressWarnings("unchecked")
            List<Element> fieldElements = element.elements("field");
            if (fieldElements.size() > 0) {
                Map<String, Object> params = new HashMap<>();
                String fieldGroupId = resultComponent.getId();
                if (StringUtils.isNotEmpty(fieldGroupId)) {
                    params.put("FieldGroup ID", fieldGroupId);
                }
                throw new GuiDevelopmentException("FieldGroup field elements should be placed within its column.",
                        context.getFullFrameId(), params);
            }
            resultComponent.setColumns(columnElements.size());

            int colIndex = 0;
            for (Element columnElement : columnElements) {
                String flex = columnElement.attributeValue("flex");
                if (StringUtils.isNotEmpty(flex)) {
                    resultComponent.setColumnExpandRatio(colIndex, Float.parseFloat(flex));
                }

                String columnWidth = loadThemeString(columnElement.attributeValue("width"));

                Iterable<FieldGroup.FieldConfig> columnFields = loadFields(resultComponent, columnElement, ds,
                        columnWidth);
                if (colIndex == 0) {
                    columnFields = Iterables.concat(columnFields, loadDynamicAttributeFields(ds));
                }
                for (FieldGroup.FieldConfig field : columnFields) {
                    resultComponent.addField(field, colIndex);
                }

                String columnFieldCaptionWidth = columnElement.attributeValue("fieldCaptionWidth");
                if (StringUtils.isNotEmpty(columnFieldCaptionWidth)) {
                    if (columnFieldCaptionWidth.startsWith(MessageTools.MARK)) {
                        columnFieldCaptionWidth = loadResourceString(columnFieldCaptionWidth);
                    }
                    if (columnFieldCaptionWidth.endsWith("px")) {
                        columnFieldCaptionWidth = columnFieldCaptionWidth.substring(0,
                                columnFieldCaptionWidth.indexOf("px"));
                    }

                    resultComponent.setFieldCaptionWidth(colIndex, Integer.parseInt(columnFieldCaptionWidth));
                }

                colIndex++;
            }
        }

        for (FieldGroup.FieldConfig field : resultComponent.getFields()) {
            if (!field.isCustom()) {
                if (!DynamicAttributesUtils.isDynamicAttribute(field.getProperty())) {
                    // the following does not make sense for dynamic attributes
                    loadValidators(resultComponent, field);
                    loadRequired(resultComponent, field);
                    loadEnable(resultComponent, field);
                }
                loadVisible(resultComponent, field);
                loadEditable(resultComponent, field);
            }
        }

        resultComponent.bind();

        for (FieldGroup.FieldConfig field : resultComponent.getFields()) {
            if (field.getXmlDescriptor() != null) {
                String generator = field.getXmlDescriptor().attributeValue("generator");
                if (generator != null) {
                    context.addPostWrapTask((boundContext, window) -> {
                        DeclarativeFieldGenerator fieldGenerator = new DeclarativeFieldGenerator(resultComponent,
                                generator);
                        Component fieldComponent = fieldGenerator.generateField(field.getTargetDatasource(),
                                field.getProperty());
                        field.setComponent(fieldComponent);
                    });
                }
            }
        }
    }

    protected void applyPermissions(Component fieldComponent) {
        if (fieldComponent instanceof DatasourceComponent) {
            DatasourceComponent dsComponent = (DatasourceComponent) fieldComponent;
            MetaPropertyPath propertyPath = dsComponent.getMetaPropertyPath();
            Datasource datasource = dsComponent.getDatasource();

            if (datasource != null && propertyPath != null) {
                MetaClass metaClass = datasource.getMetaClass();
                if (!security.isEntityAttrUpdatePermitted(metaClass, propertyPath.toString())) {
                    dsComponent.setEditable(false);
                }
                if (!security.isEntityAttrReadPermitted(metaClass, propertyPath.toString())) {
                    dsComponent.setVisible(false);
                }
            }
        }
    }

    protected List<FieldGroup.FieldConfig> loadDynamicAttributeFields(Datasource ds) {
        if (ds != null && metadataTools.isPersistent(ds.getMetaClass())) {
            String windowId = ComponentsHelper.getWindow(resultComponent).getId();

            Set<CategoryAttribute> attributesToShow = dynamicAttributesGuiTools
                    .getAttributesToShowOnTheScreen(ds.getMetaClass(), windowId, resultComponent.getId());

            if (!attributesToShow.isEmpty()) {
                List<FieldGroup.FieldConfig> fields = new ArrayList<>();

                ds.setLoadDynamicAttributes(true);

                for (CategoryAttribute attribute : attributesToShow) {
                    FieldGroup.FieldConfig field = resultComponent
                            .createField(DynamicAttributesUtils.encodeAttributeCode(attribute.getCode()));
                    field.setProperty(DynamicAttributesUtils.encodeAttributeCode(attribute.getCode()));
                    field.setCaption(attribute.getLocaleName());
                    field.setDatasource(ds);
                    field.setRequired(attribute.getRequired());
                    field.setRequiredMessage(messages.formatMainMessage("validation.required.defaultMsg",
                            attribute.getLocaleName()));
                    loadWidth(field, attribute.getWidth());

                    // Currently, ListEditor does not support datasource binding so we create custom field
                    if (BooleanUtils.isTrue(attribute.getIsCollection())) {
                        CustomFieldGenerator fieldGenerator = new DynamicAttributeCustomFieldGenerator();

                        Component fieldComponent = fieldGenerator.generateField(ds,
                                DynamicAttributesUtils.encodeAttributeCode(attribute.getCode()));
                        field.setCustom(true);
                        field.setComponent(fieldComponent);
                        applyPermissions(fieldComponent);
                    }
                    fields.add(field);
                }

                dynamicAttributesGuiTools.listenDynamicAttributesChanges(ds);
                return fields;
            }
        }
        return Collections.emptyList();
    }

    protected void loadFieldCaptionWidth(FieldGroup resultComponent, Element element) {
        String fieldCaptionWidth = element.attributeValue("fieldCaptionWidth");
        if (StringUtils.isNotEmpty(fieldCaptionWidth)) {
            if (fieldCaptionWidth.startsWith(MessageTools.MARK)) {
                fieldCaptionWidth = loadResourceString(fieldCaptionWidth);
            }
            if (fieldCaptionWidth.endsWith("px")) {
                fieldCaptionWidth = fieldCaptionWidth.substring(0, fieldCaptionWidth.indexOf("px"));
            }

            resultComponent.setFieldCaptionWidth(Integer.parseInt(fieldCaptionWidth));
        }
    }

    protected Datasource loadDatasource(Element element) {
        String datasource = element.attributeValue("datasource");
        if (!StringUtils.isBlank(datasource)) {
            Datasource ds = context.getDsContext().get(datasource);
            if (ds == null) {
                throw new GuiDevelopmentException("Can't find datasource by name: " + datasource,
                        context.getFullFrameId());
            }
            return ds;
        }
        return null;
    }

    protected List<FieldGroup.FieldConfig> loadFields(FieldGroup resultComponent, Element element, Datasource ds,
            @Nullable String columnWidth) {
        @SuppressWarnings("unchecked")
        List<Element> fieldElements = element.elements("field");
        if (!fieldElements.isEmpty()) {
            return loadFields(resultComponent, fieldElements, ds, columnWidth);
        }
        return Collections.emptyList();
    }

    protected List<FieldGroup.FieldConfig> loadFields(FieldGroup resultComponent, List<Element> elements,
            Datasource ds, @Nullable String columnWidth) {
        List<FieldGroup.FieldConfig> fields = new ArrayList<>(elements.size());
        List<String> ids = new ArrayList<>();
        for (Element fieldElement : elements) {
            FieldGroup.FieldConfig field = loadField(fieldElement, ds, columnWidth);
            if (ids.contains(field.getId())) {
                Map<String, Object> params = new HashMap<>();
                String fieldGroupId = resultComponent.getId();
                if (StringUtils.isNotEmpty(fieldGroupId)) {
                    params.put("FieldGroup ID", fieldGroupId);
                }

                throw new GuiDevelopmentException(
                        String.format("FieldGroup column contains duplicate fields '%s'.", field.getId()),
                        context.getFullFrameId(), params);
            }
            fields.add(field);
            ids.add(field.getId());
        }
        return fields;
    }

    protected CollectionDatasource findDatasourceRecursively(DsContext dsContext, String dsName) {
        if (dsContext == null) {
            return null;
        }

        Datasource datasource = dsContext.get(dsName);
        if (datasource != null && datasource instanceof CollectionDatasource) {
            return (CollectionDatasource) datasource;
        } else {
            if (dsContext.getParent() != null) {
                return findDatasourceRecursively(dsContext.getParent(), dsName);
            } else {
                return null;
            }
        }
    }

    protected FieldGroup.FieldConfig loadField(Element element, Datasource ds, String columnWidth) {
        String id = element.attributeValue("id");
        String property = element.attributeValue("property");

        if (Strings.isNullOrEmpty(id) && Strings.isNullOrEmpty(property)) {
            throw new GuiDevelopmentException(String.format(
                    "id/property is not defined for field of FieldGroup '%s'. " + "Set id or property attribute.",
                    resultComponent.getId()), context.getFullFrameId());
        }

        if (Strings.isNullOrEmpty(property)) {
            property = id;
        } else if (Strings.isNullOrEmpty(id)) {
            id = property;
        }

        Datasource targetDs = ds;

        Datasource datasource = loadDatasource(element);
        if (datasource != null) {
            targetDs = datasource;
        }

        CollectionDatasource optionsDs = null;
        String optDsName = element.attributeValue("optionsDatasource");
        if (StringUtils.isNotBlank(optDsName)) {
            DsContext dsContext = getContext().getFrame().getDsContext();
            optionsDs = findDatasourceRecursively(dsContext, optDsName);
            if (optionsDs == null) {
                throw new GuiDevelopmentException(
                        String.format("Options datasource %s not found for field %s", optDsName, id),
                        context.getFullFrameId());
            }
        }

        boolean customField = false;
        String custom = element.attributeValue("custom");
        if (StringUtils.isNotEmpty(custom)) {
            customField = Boolean.parseBoolean(custom);
        }

        if (StringUtils.isNotEmpty(element.attributeValue("generator"))) {
            customField = true;
        }

        List<Element> elements = Dom4j.elements(element);
        List<Element> customElements = elements.stream()
                .filter(e -> !("formatter".equals(e.getName()) || "validator".equals(e.getName())))
                .collect(Collectors.toList());

        if (!customElements.isEmpty()) {
            if (customElements.size() > 1) {
                throw new GuiDevelopmentException(String.format(
                        "FieldGroup field %s element cannot contains two or more custom field definitions", id),
                        context.getCurrentFrameId());
            }
            if (customField) {
                throw new GuiDevelopmentException(String.format(
                        "FieldGroup field %s cannot use both custom/generator attribute and inline component definition",
                        id), context.getCurrentFrameId());
            }
            customField = true;
        }

        if (!customField && targetDs == null) {
            throw new GuiDevelopmentException(String.format("Datasource is not defined for FieldGroup field '%s'. "
                    + "Only custom fields can have no datasource.", property), context.getFullFrameId());
        }

        FieldGroup.FieldConfig field = resultComponent.createField(id);
        if (property != null) {
            field.setProperty(property);
        }
        if (datasource != null) {
            field.setDatasource(datasource);
        }
        if (optionsDs != null) {
            field.setOptionsDatasource(optionsDs);
        }

        String stylename = element.attributeValue("stylename");
        if (StringUtils.isNotEmpty(stylename)) {
            field.setStyleName(stylename);
        }

        MetaPropertyPath metaPropertyPath = null;
        if (targetDs != null && property != null) {
            MetaClass metaClass = targetDs.getMetaClass();
            metaPropertyPath = metadataTools.resolveMetaPropertyPath(targetDs.getMetaClass(), property);
            if (metaPropertyPath == null) {
                if (!customField) {
                    throw new GuiDevelopmentException(String.format("Property '%s' is not found in entity '%s'",
                            property, metaClass.getName()), context.getFullFrameId());
                }
            }
        }
        String propertyName = metaPropertyPath != null ? metaPropertyPath.getMetaProperty().getName() : null;
        if (metaPropertyPath != null
                && DynamicAttributesUtils.isDynamicAttribute(metaPropertyPath.getMetaProperty())) {
            CategoryAttribute categoryAttribute = DynamicAttributesUtils
                    .getCategoryAttribute(metaPropertyPath.getMetaProperty());
            field.setCaption(categoryAttribute != null ? categoryAttribute.getLocaleName() : propertyName);
        } else {
            loadCaption(field, element);

            if (field.getCaption() == null) {
                field.setCaption(getDefaultCaption(field, targetDs));
            }
        }
        loadDescription(field, element);
        loadContextHelp(field, element);

        field.setXmlDescriptor(element);

        com.haulmont.cuba.gui.components.Formatter formatter = loadFormatter(element);
        if (formatter != null) {
            field.setFormatter(formatter);
        }

        String defaultWidth = element.attributeValue("width");
        if (StringUtils.isEmpty(defaultWidth)) {
            defaultWidth = columnWidth;
        }
        loadWidth(field, defaultWidth);

        if (customField) {
            field.setCustom(true);
        }

        String required = element.attributeValue("required");
        if (StringUtils.isNotEmpty(required)) {
            field.setRequired(Boolean.parseBoolean(required));
        }

        String requiredMsg = element.attributeValue("requiredMessage");
        if (requiredMsg != null) {
            requiredMsg = loadResourceString(requiredMsg);
            field.setRequiredMessage(requiredMsg);
        }

        String tabIndex = element.attributeValue("tabIndex");
        if (StringUtils.isNotEmpty(tabIndex)) {
            field.setTabIndex(Integer.parseInt(tabIndex));
        }

        if (customElements.size() == 1) {
            // load nested component defined as inline
            Element customFieldElement = customElements.get(0);

            LayoutLoader loader = new LayoutLoader(context, factory, layoutLoaderConfig);
            loader.setLocale(getLocale());
            loader.setMessagesPack(getMessagesPack());

            ComponentLoader childComponentLoader = loader.createComponent(customFieldElement);
            childComponentLoader.loadComponent();

            Component customComponent = childComponentLoader.getResultComponent();

            String inlineAttachMode = element.attributeValue("inlineAttachMode");
            if (StringUtils.isNotEmpty(inlineAttachMode)) {
                field.setComponent(customComponent, FieldGroup.FieldAttachMode.valueOf(inlineAttachMode));
            } else {
                field.setComponent(customComponent);
            }
        }

        return field;
    }

    protected void loadContextHelp(FieldGroup.FieldConfig field, Element element) {
        String contextHelpText = element.attributeValue("contextHelpText");
        if (StringUtils.isNotEmpty(contextHelpText)) {
            contextHelpText = loadResourceString(contextHelpText);
            field.setContextHelpText(contextHelpText);
        }

        String htmlEnabled = element.attributeValue("contextHelpTextHtmlEnabled");
        if (StringUtils.isNotEmpty(htmlEnabled)) {
            field.setContextHelpTextHtmlEnabled(Boolean.parseBoolean(htmlEnabled));
        }
    }

    protected String getDefaultCaption(FieldGroup.FieldConfig fieldConfig, Datasource fieldDatasource) {
        String caption = fieldConfig.getCaption();
        if (caption == null) {
            String propertyId = fieldConfig.getProperty();
            MetaPropertyPath propertyPath = fieldDatasource != null
                    ? fieldDatasource.getMetaClass().getPropertyPath(propertyId)
                    : null;

            if (propertyPath != null) {
                MetaClass propertyMetaClass = metadataTools.getPropertyEnclosingMetaClass(propertyPath);
                String propertyName = propertyPath.getMetaProperty().getName();
                caption = messageTools.getPropertyCaption(propertyMetaClass, propertyName);
            }
        }
        return caption;
    }

    protected void loadWidth(FieldGroup.FieldConfig field, String width) {
        if ("auto".equalsIgnoreCase(width)) {
            field.setWidth(Component.AUTO_SIZE);
        } else if (StringUtils.isNotBlank(width)) {
            field.setWidth(loadThemeString(width));
        }
    }

    protected void loadValidators(FieldGroup resultComponent, FieldGroup.FieldConfig field) {
        Element descriptor = field.getXmlDescriptor();
        @SuppressWarnings("unchecked")
        List<Element> validatorElements = (descriptor == null) ? null : descriptor.elements("validator");
        if (validatorElements != null) {
            if (!validatorElements.isEmpty()) {
                for (Element validatorElement : validatorElements) {
                    Field.Validator validator = loadValidator(validatorElement);
                    if (validator != null) {
                        field.addValidator(validator);
                    }
                }
            }
        } else {
            Datasource ds;
            if (field.getDatasource() == null) {
                ds = resultComponent.getDatasource();
            } else {
                ds = field.getDatasource();
            }

            if (ds != null) {
                MetaClass metaClass = ds.getMetaClass();
                MetaPropertyPath metaPropertyPath = metaClass.getPropertyPath(field.getProperty());

                if (metaPropertyPath != null) {
                    MetaProperty metaProperty = metaPropertyPath.getMetaProperty();
                    Field.Validator validator = null;
                    if (descriptor == null) {
                        validator = getDefaultValidator(metaProperty);
                    } else if (!"timeField".equals(descriptor.attributeValue("field"))) {
                        validator = getDefaultValidator(metaProperty); //In this case we no need to use validator
                    }

                    if (validator != null) {
                        field.addValidator(validator);
                    }
                }
            }
        }
    }

    protected void loadRequired(FieldGroup resultComponent, FieldGroup.FieldConfig field) {
        if (field.isCustom()) {
            Element element = field.getXmlDescriptor();
            if (element == null) {
                return;
            }

            String required = element.attributeValue("required");
            if (StringUtils.isNotEmpty(required)) {
                field.setRequired(Boolean.parseBoolean(required));
            }

            String requiredMessage = element.attributeValue("requiredMessage");
            if (StringUtils.isNotEmpty(requiredMessage)) {
                field.setRequiredMessage(loadResourceString(requiredMessage));
            }
        } else {
            MetaClass metaClass = getMetaClass(resultComponent, field);

            Element element = field.getXmlDescriptor();

            String required = element.attributeValue("required");
            if (StringUtils.isNotEmpty(required)) {
                field.setRequired(Boolean.parseBoolean(required));
            }

            String requiredMsg = element.attributeValue("requiredMessage");
            if (StringUtils.isEmpty(requiredMsg) && metaClass != null) {
                MetaPropertyPath propertyPath = metaClass.getPropertyPath(field.getProperty());

                checkNotNullArgument(propertyPath, "Could not resolve property path '%s' in '%s'",
                        field.getProperty(), metaClass);

                requiredMsg = messageTools.getDefaultRequiredMessage(metaClass, propertyPath.toString());
            }

            field.setRequiredMessage(loadResourceString(requiredMsg));
        }
    }

    @Override
    protected void loadEditable(Component component, Element element) {
        FieldGroup fieldGroup = (FieldGroup) component;

        if (fieldGroup.getDatasource() != null) {
            MetaClass metaClass = fieldGroup.getDatasource().getMetaClass();
            boolean editableByPermission = (security.isEntityOpPermitted(metaClass, EntityOp.CREATE)
                    || security.isEntityOpPermitted(metaClass, EntityOp.UPDATE));
            if (!editableByPermission) {
                fieldGroup.setEditable(false);
                return;
            }
        }

        String editable = element.attributeValue("editable");
        if (StringUtils.isNotEmpty(editable)) {
            fieldGroup.setEditable(Boolean.parseBoolean(editable));
        }
    }

    protected MetaClass getMetaClass(FieldGroup resultComponent, FieldGroup.FieldConfig field) {
        if (field.isCustom()) {
            return null;
        }
        Datasource datasource;
        if (field.getDatasource() != null) {
            datasource = field.getDatasource();
        } else if (resultComponent.getDatasource() != null) {
            datasource = resultComponent.getDatasource();
        } else {
            throw new GuiDevelopmentException(
                    String.format("Unable to get datasource for field '%s'", field.getId()),
                    context.getFullFrameId());
        }
        return datasource.getMetaClass();
    }

    protected void loadEditable(FieldGroup resultComponent, FieldGroup.FieldConfig field) {
        Element element = field.getXmlDescriptor();
        if (element != null) {
            String editable = element.attributeValue("editable");
            if (StringUtils.isNotEmpty(editable)) {
                field.setEditable(Boolean.parseBoolean(editable));
            }
        }

        if (!field.isCustom() && BooleanUtils.isNotFalse(field.isEditable())) {
            MetaClass metaClass = getMetaClass(resultComponent, field);
            MetaPropertyPath propertyPath = metadataTools.resolveMetaPropertyPath(metaClass, field.getProperty());

            checkNotNullArgument(propertyPath, "Could not resolve property path '%s' in '%s'", field.getId(),
                    metaClass);

            if (!security.isEntityAttrUpdatePermitted(metaClass, propertyPath.toString())) {
                field.setEditable(false);
            }
        }
    }

    protected void loadVisible(FieldGroup resultComponent, FieldGroup.FieldConfig field) {
        Element element = field.getXmlDescriptor();
        if (element != null) {
            String visible = element.attributeValue("visible");
            if (StringUtils.isNotEmpty(visible)) {
                field.setVisible(Boolean.parseBoolean(visible));
            }
        }

        if (!field.isCustom() && BooleanUtils.isNotFalse(field.isVisible())) {
            MetaClass metaClass = getMetaClass(resultComponent, field);
            MetaPropertyPath propertyPath = metadataTools.resolveMetaPropertyPath(metaClass, field.getProperty());

            checkNotNullArgument(propertyPath, "Could not resolve property path '%s' in '%s'", field.getId(),
                    metaClass);

            if (!security.isEntityAttrReadPermitted(metaClass, propertyPath.toString())) {
                field.setVisible(false);
            }
        }
    }

    protected void loadEnable(FieldGroup resultComponent, FieldGroup.FieldConfig field) {
        Element element = field.getXmlDescriptor();
        if (element != null) {
            String enable = element.attributeValue("enable");
            if (StringUtils.isNotEmpty(enable)) {
                field.setEnabled(Boolean.parseBoolean(enable));
            }
        }
    }

    protected void loadCaptionAlignment(FieldGroup resultComponent, Element element) {
        String captionAlignment = element.attributeValue("captionAlignment");
        if (StringUtils.isNotEmpty(captionAlignment)) {
            resultComponent.setCaptionAlignment(FieldCaptionAlignment.valueOf(captionAlignment));
        }
    }
}