com.haulmont.cuba.gui.components.EditorWindowDelegate.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.gui.components.EditorWindowDelegate.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.components;

import com.google.common.collect.Iterables;
import com.haulmont.chile.core.datatypes.Datatypes;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.cuba.client.ClientConfig;
import com.haulmont.cuba.core.app.LockService;
import com.haulmont.cuba.core.entity.BaseGenericIdEntity;
import com.haulmont.cuba.core.entity.Categorized;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.global.validation.groups.UiCrossFieldChecks;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.data.*;
import com.haulmont.cuba.gui.data.impl.CollectionPropertyDatasourceImpl;
import com.haulmont.cuba.gui.data.impl.DatasourceImplementation;
import com.haulmont.cuba.gui.data.impl.EntityCopyUtils;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.security.entity.EntityOp;

import javax.annotation.Nullable;
import javax.validation.ConstraintViolation;
import javax.validation.ElementKind;
import javax.validation.Path;
import javax.validation.Validator;
import java.util.Collection;
import java.util.Date;
import java.util.Set;

public class EditorWindowDelegate extends WindowDelegate {

    protected boolean justLocked;
    protected boolean commitActionPerformed;
    protected boolean commitAndCloseButtonExists;
    protected boolean readOnly;

    protected boolean crossFieldValidate = true;

    protected Metadata metadata = AppBeans.get(Metadata.NAME);
    protected Messages messages = AppBeans.get(Messages.NAME);
    protected UserSessionSource userSessionSource = AppBeans.get(UserSessionSource.NAME);
    protected LockService lockService = AppBeans.get(LockService.NAME);
    protected Configuration configuration = AppBeans.get(Configuration.NAME);
    protected EntityStates entityStates = AppBeans.get(EntityStates.NAME);

    public EditorWindowDelegate(Window window) {
        super(window);
    }

    @Override
    public Window wrapBy(Class<?> wrapperClass) {
        final Window.Editor editor = (Window.Editor) super.wrapBy(wrapperClass);

        final Component commitAndCloseButton = ComponentsHelper.findComponent(editor,
                Window.Editor.WINDOW_COMMIT_AND_CLOSE);
        String commitShortcut = configuration.getConfig(ClientConfig.class).getCommitShortcut();
        if (commitAndCloseButton != null) {
            commitAndCloseButtonExists = true;

            this.window.addAction(new AbstractAction(Window.Editor.WINDOW_COMMIT_AND_CLOSE, commitShortcut) {
                @Override
                public String getCaption() {
                    return messages.getMainMessage("actions.OkClose");
                }

                @Override
                public void actionPerform(Component component) {
                    editor.commitAndClose();
                }
            });
        }

        AbstractAction commitAction = new AbstractAction(Window.Editor.WINDOW_COMMIT) {
            @Override
            public String getCaption() {
                return messages.getMainMessage(commitAndCloseButtonExists ? "actions.Save" : "actions.Ok");
            }

            @Override
            public void actionPerform(Component component) {
                if (!commitAndCloseButtonExists) {
                    editor.commitAndClose();
                } else {
                    if (editor.commit()) {
                        commitActionPerformed = true;
                    }
                }
            }
        };
        if (!commitAndCloseButtonExists) {
            commitAction.setShortcut(commitShortcut);
        }
        this.window.addAction(commitAction);

        this.window.addAction(new AbstractAction(Window.Editor.WINDOW_CLOSE) {
            @Override
            public String getCaption() {
                return messages.getMainMessage("actions.Cancel");
            }

            @Override
            public void actionPerform(Component component) {
                editor.close(commitActionPerformed ? Window.COMMIT_ACTION_ID : getId());
            }
        });

        return editor;
    }

    public Entity getItem() {
        return getDatasource().getItem();
    }

    @SuppressWarnings("unchecked")
    public void setItem(Entity item) {
        Datasource ds = getDatasource();
        DataSupplier dataservice = ds.getDataSupplier();

        DatasourceImplementation parentDs = (DatasourceImplementation) ((DatasourceImplementation) ds).getParent();

        DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.NAME);
        if (dynamicAttributesGuiTools.screenContainsDynamicAttributes(ds.getView(), getWrapper().getId())) {
            ds.setLoadDynamicAttributes(true);
        }

        Class<? extends Entity> entityClass = item.getClass();
        Object entityId = item.getId();

        if (parentDs != null) {
            if (!PersistenceHelper.isNew(item) && !parentDs.getItemsToCreate().contains(item)
                    && !parentDs.getItemsToUpdate().contains(item) && parentDs instanceof CollectionDatasource
                    && ((CollectionDatasource) parentDs).containsItem(item.getId())
                    && !entityStates.isLoadedWithView(item, ds.getView())) {
                item = dataservice.reload(item, ds.getView(), ds.getMetaClass(), ds.getLoadDynamicAttributes());
                if (parentDs instanceof CollectionPropertyDatasourceImpl) {
                    ((CollectionPropertyDatasourceImpl) parentDs).replaceItem(item);
                } else {
                    ((CollectionDatasource) parentDs).updateItem(item);
                }
            }
            item = EntityCopyUtils.copyCompositions(item);
            handlePreviouslyDeletedCompositionItems(item, parentDs);
        } else if (!PersistenceHelper.isNew(item)) {
            item = dataservice.reload(item, ds.getView(), ds.getMetaClass(), ds.getLoadDynamicAttributes());
        }

        if (item == null) {
            throw new EntityAccessException(entityClass, entityId);
        }

        if (PersistenceHelper.isNew(item) && !ds.getMetaClass().equals(item.getMetaClass())) {
            Entity newItem = ds.getDataSupplier().newInstance(ds.getMetaClass());
            metadata.getTools().copy(item, newItem);
            item = newItem;
        }

        if (ds.getLoadDynamicAttributes() && item instanceof BaseGenericIdEntity) {
            if (PersistenceHelper.isNew(item)) {
                dynamicAttributesGuiTools.initDefaultAttributeValues((BaseGenericIdEntity) item,
                        item.getMetaClass());
            }
            if (item instanceof Categorized) {
                dynamicAttributesGuiTools.listenCategoryChanges(ds);
            }
        }

        ds.setItem(item);

        if (PersistenceHelper.isNew(item)) {
            // The new item may contain references which were created in initNewItem() and are also new. Below we
            // make sure that they will be saved on commit.
            for (Datasource datasource : ds.getDsContext().getAll()) {
                if (datasource instanceof NestedDatasource && ((NestedDatasource) datasource).getMaster() == ds) {
                    if (datasource.getItem() != null && PersistenceHelper.isNew(datasource.getItem()))
                        ((DatasourceImplementation) datasource).modified(datasource.getItem());
                }
            }
        }

        ((DatasourceImplementation) ds).setModified(false);

        Security security = AppBeans.get(Security.NAME);
        if (!PersistenceHelper.isNew(item) && security.isEntityOpPermitted(ds.getMetaClass(), EntityOp.UPDATE)) {
            readOnly = false;
            LockInfo lockInfo = lockService.lock(getMetaClassForLocking(ds).getName(), item.getId().toString());
            if (lockInfo == null) {
                justLocked = true;
            } else if (!(lockInfo instanceof LockNotSupported)) {
                window.getWindowManager()
                        .showNotification(messages.getMainMessage("entityLocked.msg"),
                                String.format(messages.getMainMessage("entityLocked.desc"),
                                        lockInfo.getUser().getLogin(), Datatypes.getNN(Date.class)
                                                .format(lockInfo.getSince(), userSessionSource.getLocale())),
                                Frame.NotificationType.HUMANIZED);
                Action action = window.getAction(Window.Editor.WINDOW_COMMIT);
                if (action != null)
                    action.setEnabled(false);
                action = window.getAction(Window.Editor.WINDOW_COMMIT_AND_CLOSE);
                if (action != null)
                    action.setEnabled(false);
                readOnly = true;
            }
        }
    }

    /**
     * This method is required for multi-level composition, when a user deletes records from nested editors, saves them
     * and then reopens. When an editor is opened, we reload the item from the database, hence we need to remove
     * nested items previously deleted by the user.
     */
    protected void handlePreviouslyDeletedCompositionItems(Entity entity, final DatasourceImplementation parentDs) {
        for (MetaProperty property : metadata.getClassNN(entity.getClass()).getProperties()) {
            if (!PersistenceHelper.isLoaded(entity, property.getName()))
                return;

            if (property.getType() == MetaProperty.Type.COMPOSITION) {
                for (Datasource datasource : parentDs.getDsContext().getAll()) {
                    if (datasource instanceof NestedDatasource
                            && ((NestedDatasource) datasource).getMaster().equals(parentDs)) {
                        Object value = entity.getValue(property.getName());
                        if (value instanceof Collection) {
                            Collection collection = (Collection) value;
                            //noinspection unchecked
                            collection.removeAll(((DatasourceImplementation) datasource).getItemsToDelete());
                        }
                    }
                }
            }
        }
    }

    public void setParentDs(Datasource parentDs) {
        Datasource ds = getDatasource();
        ((DatasourceImplementation) ds).setParent(parentDs);
    }

    @Nullable
    public Datasource getParentDs() {
        Datasource ds = getDatasource();
        return ((DatasourceImplementation) ds).getParent();
    }

    @Override
    public boolean isModified() {
        if (readOnly)
            return false;
        else
            return super.isModified();
    }

    public boolean commit(boolean close) {
        if (wrapper instanceof AbstractEditor && !((AbstractEditor) wrapper).preCommit())
            return false;

        boolean committed;
        final DsContext context = window.getDsContext();
        if (context != null) {
            committed = context.commit();
        } else {
            DataSupplier supplier = getDataService();
            supplier.commit(getItem());
            committed = true;
        }

        return !(wrapper instanceof AbstractEditor) || ((AbstractEditor) wrapper).postCommit(committed, close);
    }

    protected DataSupplier getDataService() {
        final DsContext context = window.getDsContext();
        if (context == null) {
            throw new UnsupportedOperationException();
        } else {
            return context.getDataSupplier();
        }
    }

    public boolean isLocked() {
        return !justLocked;
    }

    public void releaseLock() {
        if (justLocked) {
            Datasource ds = getDatasource();
            Entity entity = ds.getItem();
            if (entity != null) {
                lockService.unlock(getMetaClassForLocking(ds).getName(), entity.getId().toString());
            }
        }
    }

    protected MetaClass getMetaClassForLocking(Datasource ds) {
        // lock original metaClass, if any, because by convention all the configuration is based on original entities
        MetaClass metaClass = metadata.getExtendedEntities().getOriginalMetaClass(ds.getMetaClass());
        if (metaClass == null) {
            metaClass = ds.getMetaClass();
        }
        return metaClass;
    }

    public boolean isCrossFieldValidate() {
        return crossFieldValidate;
    }

    public void setCrossFieldValidate(boolean crossFieldValidate) {
        this.crossFieldValidate = crossFieldValidate;
    }

    public void validateAdditionalRules(ValidationErrors errors) {
        // all previous validations return no errors
        if (crossFieldValidate && errors.isEmpty()) {
            BeanValidation beanValidation = AppBeans.get(BeanValidation.NAME);

            Validator validator = beanValidation.getValidator();
            Set<ConstraintViolation<Entity>> violations = validator.validate(getItem(), UiCrossFieldChecks.class);

            violations.stream().filter(violation -> {
                Path propertyPath = violation.getPropertyPath();

                Path.Node lastNode = Iterables.getLast(propertyPath);
                return lastNode.getKind() == ElementKind.BEAN;
            }).forEach(violation -> errors.add(violation.getMessage()));
        }
    }
}