org.activityinfo.ui.client.page.config.design.DesignPresenter.java Source code

Java tutorial

Introduction

Here is the source code for org.activityinfo.ui.client.page.config.design.DesignPresenter.java

Source

package org.activityinfo.ui.client.page.config.design;

/*
 * #%L
 * ActivityInfo Server
 * %%
 * Copyright (C) 2009 - 2013 UNICEF
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.TreeStore;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.menu.MenuItem;
import com.google.common.base.Function;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.ImplementedBy;
import com.google.inject.Inject;
import org.activityinfo.i18n.shared.I18N;
import org.activityinfo.i18n.shared.UiConstants;
import org.activityinfo.legacy.client.Dispatcher;
import org.activityinfo.legacy.client.callback.SuccessCallback;
import org.activityinfo.legacy.client.monitor.MaskingAsyncMonitor;
import org.activityinfo.legacy.client.state.StateProvider;
import org.activityinfo.legacy.shared.command.*;
import org.activityinfo.legacy.shared.command.result.CreateResult;
import org.activityinfo.legacy.shared.command.result.VoidResult;
import org.activityinfo.legacy.shared.model.*;
import org.activityinfo.model.resource.ResourceId;
import org.activityinfo.ui.client.AppEvents;
import org.activityinfo.ui.client.EventBus;
import org.activityinfo.ui.client.page.NavigationEvent;
import org.activityinfo.ui.client.page.NavigationHandler;
import org.activityinfo.ui.client.page.PageId;
import org.activityinfo.ui.client.page.PageState;
import org.activityinfo.ui.client.page.common.dialog.FormDialogCallback;
import org.activityinfo.ui.client.page.common.dialog.FormDialogTether;
import org.activityinfo.ui.client.page.common.grid.AbstractEditorGridPresenter;
import org.activityinfo.ui.client.page.common.grid.TreeGridView;
import org.activityinfo.ui.client.page.common.toolbar.UIActions;
import org.activityinfo.ui.client.page.config.DbPage;
import org.activityinfo.ui.client.page.config.DbPageState;
import org.activityinfo.ui.client.page.config.design.dialog.NewFormDialog;
import org.activityinfo.ui.client.page.config.design.importer.SchemaImportDialog;
import org.activityinfo.ui.client.page.config.design.importer.SchemaImporter;
import org.activityinfo.ui.client.page.instance.InstancePage;
import org.activityinfo.ui.client.page.instance.InstancePlace;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Objects;

/**
 * Presenter for the Design Page, which enables the user to define UserDatabases
 * and their Activities, Attributes, and Indicators.
 *
 * @author Alex Bertram
 */
public class DesignPresenter extends AbstractEditorGridPresenter<ModelData> implements DbPage {
    public static final PageId PAGE_ID = new PageId("design");

    @ImplementedBy(DesignView.class)
    public interface View extends TreeGridView<DesignPresenter, ModelData> {
        public void init(DesignPresenter presenter, UserDatabaseDTO db, TreeStore store);

        public FormDialogTether showNewForm(EntityDTO entity, FormDialogCallback callback);

        public Menu getNewMenu();

        public MenuItem getNewAttributeGroup();

        public MenuItem getNewAttribute();

        public MenuItem getNewIndicator();

        public void showForm(ModelData model);
    }

    private final EventBus eventBus;
    private final Dispatcher service;
    private final View view;
    private final UiConstants messages;

    private UserDatabaseDTO db;
    private TreeStore<ModelData> treeStore;

    @Inject
    public DesignPresenter(EventBus eventBus, Dispatcher service, StateProvider stateMgr, View view,
            UiConstants messages) {
        super(eventBus, service, stateMgr, view);
        this.eventBus = eventBus;
        this.service = service;
        this.view = view;
        this.messages = messages;
    }

    @Override
    public void go(UserDatabaseDTO db) {

        this.db = db;

        treeStore = new TreeStore<>();
        fillStore(messages);

        initListeners(treeStore, null);

        this.view.init(this, db, treeStore);
        this.view.setActionEnabled(UIActions.DELETE, false);
        this.view.setActionEnabled(UIActions.EDIT, false);
        this.view.setActionEnabled(UIActions.OPEN_TABLE, false);

        initMenu();
    }

    private void initMenu() {
        Menu newMenu = this.view.getNewMenu();
        if (newMenu == null) {
            return;
        }

        newMenu.addListener(Events.BeforeShow, new Listener<BaseEvent>() {
            @Override
            public void handleEvent(BaseEvent be) {

                ModelData sel = DesignPresenter.this.view.getSelection();
                IsActivityDTO activity = DesignPresenter.this.getSelectedActivity(sel);

                DesignPresenter.this.view.getNewAttributeGroup()
                        .setEnabled(activity != null && activity.getClassicView());
                DesignPresenter.this.view.getNewAttribute()
                        .setEnabled(activity != null
                                && (sel instanceof AttributeGroupDTO || sel instanceof AttributeDTO)
                                && activity.getClassicView());
                DesignPresenter.this.view.getNewIndicator()
                        .setEnabled(activity != null && activity.getClassicView());
            }
        });
    }

    public void refresh() {
        service.execute(new GetSchema(), new MaskingAsyncMonitor((ContentPanel) view, I18N.CONSTANTS.loading()),
                new AsyncCallback<SchemaDTO>() {
                    @Override
                    public void onFailure(Throwable caught) {
                        //handled by masking async monitor
                    }

                    @Override
                    public void onSuccess(SchemaDTO result) {
                        db = result.getDatabaseById(db.getId());
                        fillStore(messages);
                        DesignPresenter.this.view.showForm(DesignPresenter.this.view.getSelection());
                    }
                });
    }

    @Override
    public void shutdown() {

    }

    private void fillStore(UiConstants messages) {

        treeStore.removeAll();

        for (ActivityDTO activity : db.getActivities()) {
            ActivityDTO activityNode = new ActivityDTO(activity);
            treeStore.add(activityNode, false);

            if (!activityNode.getClassicView()) {
                continue; // skip indicators and attributes in tree if activity is not classicView=true
            }

            final AttributeGroupFolder attributeFolder = new AttributeGroupFolder(messages.attributes());
            treeStore.add(activityNode, attributeFolder, false);

            final IndicatorFolder indicatorFolder = new IndicatorFolder(messages.indicators());
            treeStore.add(activityNode, indicatorFolder, false);

            service.execute(new GetActivityForm(activity.getId())).then(new SuccessCallback<ActivityFormDTO>() {
                @Override
                public void onSuccess(ActivityFormDTO activityForm) {
                    for (AttributeGroupDTO group : activityForm.getAttributeGroups()) {
                        if (group != null) {
                            AttributeGroupDTO groupNode = new AttributeGroupDTO(group);
                            treeStore.add(attributeFolder, groupNode, false);

                            for (AttributeDTO attribute : group.getAttributes()) {
                                AttributeDTO attributeNode = new AttributeDTO(attribute);
                                treeStore.add(groupNode, attributeNode, false);
                            }
                        }
                    }

                    for (IndicatorGroup group : activityForm.groupIndicators()) {
                        for (IndicatorDTO indicator : group.getIndicators()) {
                            IndicatorDTO indicatorNode = new IndicatorDTO(indicator);
                            treeStore.add(indicatorFolder, indicatorNode, false);
                        }
                    }
                }
            });

        }

        for (LocationTypeDTO locationType : db.getCountry().getLocationTypes()) {
            if (Objects.equals(locationType.getDatabaseId(), db.getId())) {
                treeStore.add(locationType, false);
            }
        }
    }

    @Override
    public Store<ModelData> getStore() {
        return treeStore;
    }

    public TreeStore<ModelData> getTreeStore() {
        return treeStore;
    }

    @Override
    public boolean navigate(PageState place) {
        return place instanceof DbPageState && place.getPageId().equals(PAGE_ID)
                && ((DbPageState) place).getDatabaseId() == db.getId();
    }

    @Override
    public void onUIAction(String actionId) {
        super.onUIAction(actionId);

        if (UIActions.EXPORT.equals(actionId)) {
            Window.open("/resources/database/" + db.getId() + "/schema.csv", "_blank", null);

        } else if (UIActions.IMPORT.equals(actionId)) {
            SchemaImporter importer = new SchemaImporter(service, db);
            SchemaImportDialog dialog = new SchemaImportDialog(importer);
            dialog.show().then(new Function<Void, Object>() {
                @Nullable
                @Override
                public Object apply(@Nullable Void input) {
                    refresh();
                    return null;
                }
            });
        } else if (UIActions.EDIT.equals(actionId)) {
            ResourceId formClassId = getSelectedActivity(view.getSelection()).getFormClassId();
            eventBus.fireEvent(new NavigationEvent(NavigationHandler.NAVIGATION_REQUESTED,
                    new InstancePlace(formClassId, InstancePage.DESIGN_PAGE_ID)));

        } else if (UIActions.OPEN_TABLE.equals(actionId)) {
            IsFormClass formClass = (IsFormClass) view.getSelection();
            eventBus.fireEvent(new NavigationEvent(NavigationHandler.NAVIGATION_REQUESTED,
                    new InstancePlace(formClass.getResourceId(), InstancePage.TABLE_PAGE_ID)));
        }
    }

    public void onNodeDropped(ModelData source) {

        // update sortOrder

        ModelData parent = treeStore.getParent(source);
        List<ModelData> children = parent == null ? treeStore.getRootItems() : treeStore.getChildren(parent);

        for (int i = 0; i != children.size(); ++i) {
            Record record = treeStore.getRecord(children.get(i));
            record.set("sortOrder", i);
        }
    }

    public void onNew(String entityName) {

        final EntityDTO newEntity;
        ModelData parent;

        ModelData selected = view.getSelection();

        if ("Activity".equals(entityName)) {
            final NewFormDialog newFormDialog = new NewFormDialog();
            newFormDialog.show();
            newFormDialog.setSuccessHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    createNewActivity(newFormDialog);
                }
            });
            return;

        } else if ("LocationType".equals(entityName)) {
            newEntity = new LocationTypeDTO();
            newEntity.set("databaseId", db.getId());
            parent = null;

        } else if ("AttributeGroup".equals(entityName)) {
            IsActivityDTO activity = findActivityFolder(selected);

            newEntity = new AttributeGroupDTO();
            newEntity.set("activityId", activity.getId());
            parent = treeStore.getChild((ModelData) activity, 0);

        } else if ("Attribute".equals(entityName)) {
            AttributeGroupDTO group = findAttributeGroupNode(selected);

            newEntity = new AttributeDTO();
            newEntity.set("attributeGroupId", group.getId());

            parent = group;

        } else if ("Indicator".equals(entityName)) {
            IsActivityDTO activity = findActivityFolder(selected);

            IndicatorDTO newIndicator = new IndicatorDTO();
            newIndicator.setAggregation(IndicatorDTO.AGGREGATE_SUM);

            if (activity instanceof ActivityFormDTO) {
                newIndicator.set("sortOrder", ((ActivityFormDTO) activity).getIndicators().size() + 1);
            }

            newEntity = newIndicator;
            newEntity.set("activityId", activity.getId());

            parent = treeStore.getChild((ModelData) activity, 1);

        } else {
            return; // TODO log error
        }

        createEntity(parent, newEntity);
    }

    private void createNewActivity(NewFormDialog newFormDialog) {
        final ActivityFormDTO newActivity = new ActivityFormDTO(db);
        newActivity.set("databaseId", db.getId());
        newActivity.setName(newFormDialog.getName());
        newActivity.setCategory(newFormDialog.getCategory());

        if (newFormDialog.getViewType() == NewFormDialog.ViewType.CLASSIC
                || newFormDialog.getViewType() == NewFormDialog.ViewType.CLASSIC_MONTHLY) {

            newActivity.setClassicView(true);
            newActivity.setReportingFrequency(
                    newFormDialog.getViewType() == NewFormDialog.ViewType.CLASSIC ? ActivityFormDTO.REPORT_ONCE
                            : ActivityFormDTO.REPORT_MONTHLY);

            createEntity(null, newActivity);
            return;
        } else if (newFormDialog.getViewType() == NewFormDialog.ViewType.NEW_FORM_DESIGNER) {

            newActivity.setClassicView(false);
            newActivity.setReportingFrequency(ActivityFormDTO.REPORT_ONCE);
            newActivity.setLocationType(newActivityLocationTypeForModernView());

            service.execute(new CreateEntity(newActivity), new SuccessCallback<CreateResult>() {
                @Override
                public void onSuccess(CreateResult result) {

                    newActivity.setId(result.getNewId());

                    eventBus.fireEvent(new NavigationEvent(NavigationHandler.NAVIGATION_REQUESTED,
                            new InstancePlace(newActivity.getResourceId(), InstancePage.DESIGN_PAGE_ID)));
                }
            });

            return;
        }

        throw new RuntimeException("Unsupported view type of activity: " + newFormDialog.getViewType());
    }

    private LocationTypeDTO newActivityLocationTypeForModernView() {
        for (LocationTypeDTO dto : db.getCountry().getLocationTypes()) {
            if (dto.isNationwide()) {
                return dto;
            }
        }
        //        throw new RuntimeException("Failed to find nationwide location type, db:" + db + ", country:" + db.getCountry());
        // return first location, test db doesn't have nationwide location type for country
        return db.getCountry().getLocationTypes().get(0);
    }

    private void createEntity(final ModelData parent, final EntityDTO newEntity) {
        view.showNewForm(newEntity, new FormDialogCallback() {
            @Override
            public void onValidated(final FormDialogTether tether) {

                service.execute(new CreateEntity(newEntity), tether, new AsyncCallback<CreateResult>() {
                    @Override
                    public void onFailure(Throwable caught) {
                        GWT.log(caught.getMessage());
                    }

                    @Override
                    public void onSuccess(CreateResult result) {
                        newEntity.set("id", result.getNewId()); // todo add
                        // setId to
                        // EntityDTO
                        // interface

                        if (parent == null) {
                            treeStore.add(newEntity, false);
                        } else {
                            treeStore.add(parent, newEntity, false);
                        }

                        if (newEntity instanceof IsActivityDTO) {
                            treeStore.add(newEntity, new AttributeGroupFolder(messages.attributes()), false);
                            treeStore.add(newEntity, new IndicatorFolder(messages.indicators()), false);
                        }

                        tether.hide();

                        eventBus.fireEvent(AppEvents.SCHEMA_CHANGED);
                    }
                });

            }
        });
    }

    protected IsActivityDTO findActivityFolder(ModelData selected) {

        while (!(selected instanceof IsActivityDTO)) {
            selected = treeStore.getParent(selected);
        }

        return (IsActivityDTO) selected;
    }

    protected AttributeGroupDTO findAttributeGroupNode(ModelData selected) {
        if (selected instanceof AttributeGroupDTO) {
            return (AttributeGroupDTO) selected;
        }
        if (selected instanceof AttributeDTO) {
            return (AttributeGroupDTO) treeStore.getParent(selected);
        }
        throw new AssertionError("not a valid selection to add an attribute !");

    }

    @Override
    protected void onDeleteConfirmed(final ModelData model) {
        service.execute(new Delete((EntityDTO) model), view.getDeletingMonitor(), new AsyncCallback<VoidResult>() {
            @Override
            public void onFailure(Throwable caught) {

            }

            @Override
            public void onSuccess(VoidResult result) {
                treeStore.remove(model);
                eventBus.fireEvent(AppEvents.SCHEMA_CHANGED);
            }
        });
    }

    @Override
    protected String getStateId() {
        return "Design" + db.getId();
    }

    @Override
    protected Command createSaveCommand() {
        BatchCommand batch = new BatchCommand();

        for (ModelData model : treeStore.getRootItems()) {
            prepareBatch(batch, model);
        }
        return batch;
    }

    protected void prepareBatch(BatchCommand batch, ModelData model) {
        if (model instanceof EntityDTO) {
            Record record = treeStore.getRecord(model);
            if (record.isDirty()) {
                batch.add(new UpdateEntity((EntityDTO) model, this.getChangedProperties(record)));
            }
        }

        for (ModelData child : treeStore.getChildren(model)) {
            prepareBatch(batch, child);
        }
    }

    @Override
    public void onSelectionChanged(ModelData selectedItem) {
        view.setActionEnabled(UIActions.EDIT, this.db.isDesignAllowed() && canEditWithFormDesigner(selectedItem));
        view.setActionEnabled(UIActions.DELETE, this.db.isDesignAllowed() && selectedItem instanceof EntityDTO);
        view.setActionEnabled(UIActions.OPEN_TABLE, getSelectedActivity(selectedItem) != null);
    }

    private boolean canEditWithFormDesigner(ModelData selectedItem) {
        IsActivityDTO activity = getSelectedActivity(selectedItem);
        return activity != null && activity.getReportingFrequency() == ActivityFormDTO.REPORT_ONCE;
    }

    private IsActivityDTO getSelectedActivity(ModelData selectedItem) {
        if (selectedItem instanceof IsActivityDTO) {
            return (IsActivityDTO) selectedItem;
        } else if (selectedItem instanceof AttributeGroupFolder || selectedItem instanceof IndicatorFolder
                || selectedItem instanceof AttributeGroupDTO || selectedItem instanceof IndicatorDTO
                || selectedItem instanceof LocationTypeDTO || selectedItem instanceof AttributeDTO) {
            return getSelectedActivity(treeStore.getParent(selectedItem));
        }
        return null;
    }

    @Override
    public PageId getPageId() {
        return PAGE_ID;
    }

    @Override
    public Object getWidget() {
        return view;
    }

    @Override
    protected void onSaved() {
        eventBus.fireEvent(AppEvents.SCHEMA_CHANGED);
        refresh();
    }
}