org.kuali.student.common.ui.client.configurable.mvc.LayoutController.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.student.common.ui.client.configurable.mvc.LayoutController.java

Source

/**
 * Copyright 2010 The Kuali Foundation Licensed under the
 * Educational Community 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.osedu.org/licenses/ECL-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.kuali.student.common.ui.client.configurable.mvc;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.kuali.student.common.ui.client.application.Application;
import org.kuali.student.common.ui.client.configurable.mvc.layouts.ViewLayoutController;
import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
import org.kuali.student.common.ui.client.event.ActionEvent;
import org.kuali.student.common.ui.client.event.SaveActionEvent;
import org.kuali.student.common.ui.client.event.SectionUpdateEvent;
import org.kuali.student.common.ui.client.event.SectionUpdateHandler;
import org.kuali.student.common.ui.client.event.ValidateRequestEvent;
import org.kuali.student.common.ui.client.event.ValidateRequestHandler;
import org.kuali.student.common.ui.client.mvc.ActionCompleteCallback;
import org.kuali.student.common.ui.client.mvc.Callback;
import org.kuali.student.common.ui.client.mvc.Controller;
import org.kuali.student.common.ui.client.mvc.DataModel;
import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
import org.kuali.student.common.ui.client.mvc.View;
import org.kuali.student.common.ui.client.mvc.history.HistoryManager;
import org.kuali.student.common.ui.client.widgets.KSButton;
import org.kuali.student.common.ui.client.widgets.KSLightBox;
import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
import org.kuali.student.r1.common.assembly.data.QueryPath;
import org.kuali.student.r2.common.dto.ValidationResultInfo;
import org.kuali.student.r2.common.infc.ValidationResult.ErrorLevel;

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.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * The LayoutController is a central piece of the UIF.  This controller is also itself a view.
 * As such, LayoutControllers can also have other LayoutControllers as their views.  
 * 
 * @see Controller
 * @author Kuali Student Team
 *
 */
public abstract class LayoutController extends Controller implements ViewLayoutController, View {

    protected Map<Enum<?>, View> viewMap = new LinkedHashMap<Enum<?>, View>();
    protected Map<String, Enum<?>> viewEnumMap = new HashMap<String, Enum<?>>();
    protected Enum<?> defaultView;

    protected String name;
    protected Enum<?> viewType;

    protected View startPopupView;
    protected KSLightBox startViewWindow;

    /**
     * Constructor
     * Sets up event handlers fro section update events and validation request events.
     */
    public LayoutController() {
        super();
        //Global section update Event handling
        addApplicationEventHandler(SectionUpdateEvent.TYPE, new SectionUpdateHandler() {

            @Override
            public void onSectionUpdate(final SectionUpdateEvent event) {
                LayoutController.this.requestModel(new ModelRequestCallback<DataModel>() {

                    @Override
                    public void onRequestFail(Throwable cause) {
                        GWT.log("Unable to retrieve model for section update", cause);
                    }

                    @Override
                    public void onModelReady(DataModel model) {
                        event.getSection().updateModel(model);
                        event.getSection().updateWidgetData(model);

                    }
                });

            }
        });
        //Global validation Event handling
        addApplicationEventHandler(ValidateRequestEvent.TYPE, new ValidateRequestHandler() {

            @Override
            public void onValidateRequest(final ValidateRequestEvent event) {
                FieldDescriptor originatingField = event.getFieldDescriptor();
                String modelId = null;
                if (originatingField != null) {
                    modelId = originatingField.getModelId();
                }
                if (modelId == null) {
                    requestModel(new ModelRequestCallback<DataModel>() {
                        @Override
                        public void onModelReady(DataModel model) {
                            validate(model, event);
                        }

                        @Override
                        public void onRequestFail(Throwable cause) {
                            GWT.log("Unable to retrieve model for validation", cause);
                        }

                    });
                } else {
                    requestModel(modelId, new ModelRequestCallback<DataModel>() {
                        @Override
                        public void onModelReady(DataModel model) {
                            validate(model, event);
                        }

                        @Override
                        public void onRequestFail(Throwable cause) {
                            GWT.log("Unable to retrieve model for validation", cause);
                        }

                    });
                }
            }

        });
    }

    private void validate(DataModel model, final ValidateRequestEvent event) {
        if (event.validateSingleField()) {
            model.validateField(event.getFieldDescriptor(), new Callback<List<ValidationResultInfo>>() {
                @Override
                public void exec(List<ValidationResultInfo> result) {
                    if (event.getFieldDescriptor() != null) {
                        // We dont need to traverse since it is single field, so don't do isValid call here
                        // instead add the error messages directly
                        FieldElement element = event.getFieldDescriptor().getFieldElement();
                        Widget w = event.getFieldDescriptor().getFieldWidget();
                        if (element != null) {
                            element.clearValidationErrors();

                            if ((w instanceof CanProcessValidationResults)
                                    && ((CanProcessValidationResults) w).doesOnTheFlyValidation()) {
                                ((CanProcessValidationResults) w).Validate(event, result);
                            } else {
                                for (int i = 0; i < result.size(); i++) {
                                    ValidationResultInfo vr = result.get(i);
                                    if (vr.getElement().equals(event.getFieldDescriptor().getFieldKey())
                                            && event.getFieldDescriptor().hasHadFocus()) {
                                        element.processValidationResult(vr);
                                    }
                                }
                            }
                        }
                    }

                }
            });
        } else {
            model.validate(new Callback<List<ValidationResultInfo>>() {
                @Override
                public void exec(List<ValidationResultInfo> result) {
                    isValid(result, false, true);
                }
            });
        }
    }

    /**
     * Check to see if the list of validation results have an error.
     * @param list
     * @return
     */
    public ErrorLevel checkForErrors(List<ValidationResultInfo> list) {
        ErrorLevel errorLevel = ErrorLevel.OK;

        for (ValidationResultInfo vr : list) {
            if (vr.getErrorLevel().getLevel() > errorLevel.getLevel()) {
                errorLevel = vr.getErrorLevel();
            }
            if (errorLevel.equals(ErrorLevel.ERROR)) {
                break;
            }
        }

        return errorLevel;

    }

    /**
     * Finds the first parent LayoutController of this LayoutController, returns null if this
     * is the top level LayoutController.
     * @param w
     * @return
     */
    public static LayoutController findParentLayout(Widget w) {
        LayoutController result = null;
        while (true) {
            if (w == null) {
                break;
            } else if (w instanceof HasLayoutController) {
                result = ((HasLayoutController) w).getLayoutController();
                if (result != null) {
                    break;
                }
            } else if (w instanceof LayoutController) {
                result = (LayoutController) w;
                break;
            }
            w = w.getParent();

        }
        return result;
    }

    /**
     * @see org.kuali.student.common.ui.client.configurable.mvc.layouts.ViewLayoutController#addStartViewPopup(org.kuali.student.common.ui.client.mvc.View)
     */
    public void addStartViewPopup(final View view) {
        startPopupView = view;
        if (startViewWindow == null) {
            startViewWindow = new KSLightBox();
        }

        FlowPanel panel = new FlowPanel();
        panel.add(view.asWidget());
        KSButton save = new KSButton("Save", new ClickHandler() {
            public void onClick(ClickEvent event) {
                view.updateModel();
                SaveActionEvent saveActionEvent = new SaveActionEvent(true);

                saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback() {
                    public void onActionComplete(ActionEvent action) {
                        startViewWindow.hide();
                    }
                });

                fireApplicationEvent(saveActionEvent);
            }
        });
        startViewWindow.addButton(save);

        KSButton cancel = new KSButton("Cancel", new ClickHandler() {
            public void onClick(ClickEvent event) {
                startViewWindow.hide();
            }
        });
        startViewWindow.addButton(cancel);

        if (view instanceof SectionView) {
            ((SectionView) view).setController(this);
        }
        startViewWindow.setWidget(panel);
    }

    /**
     * @return true if the start popup is showing
     */
    public boolean isStartViewShowing() {
        if (startViewWindow == null) {
            return false;
        }
        return startViewWindow.isShowing();
    }

    public View getStartPopupView() {
        return startPopupView;
    }

    public void showStartPopup(final Callback<Boolean> onReadyCallback) {
        startPopupView.beforeShow(new Callback<Boolean>() {
            @Override
            public void exec(Boolean result) {
                if (result) {
                    startViewWindow.show();
                }
                onReadyCallback.exec(result);
            }
        });
    }

    public KSLightBox getStartPopup() {
        return startViewWindow;
    }

    /*New methods*/

    /**
     * @see org.kuali.student.common.ui.client.configurable.mvc.layouts.ViewLayoutController#addView(org.kuali.student.common.ui.client.mvc.View)
     */
    public void addView(View view) {
        viewMap.put(view.getViewEnum(), view);
        viewEnumMap.put(view.getViewEnum().toString(), view.getViewEnum());
        if (view instanceof SectionView) {
            ((SectionView) view).setController(this);
        } else if (view instanceof ToolView) {
            ((ToolView) view).setController(this);
        }
    }

    /**
     * @see org.kuali.student.common.ui.client.configurable.mvc.layouts.ViewLayoutController#setDefaultView(java.lang.Enum)
     */
    public <V extends Enum<?>> void setDefaultView(V viewType) {
        this.defaultView = viewType;
    }

    public Enum<?> getDefaultView() {
        return this.defaultView;
    }

    /**
     * @see org.kuali.student.common.ui.client.mvc.View#updateModel()
     */
    public abstract void updateModel();

    /**
     * Update the model with a single views information
     * @param viewType
     */
    public void updateModelFromView(Enum<?> viewType) {
        View v = viewMap.get(viewType);
        if (v != null) {
            v.updateModel();
        }
    }

    /**
     * Update a the model from the view that is currently being shown by this controller
     */
    public void updateModelFromCurrentView() {
        if (this.getCurrentView() != null) {
            this.getCurrentView().updateModel();
        }
    }

    @Override
    public <V extends Enum<?>> void getView(V viewType, Callback<View> callback) {
        callback.exec(viewMap.get(viewType));
    }

    @Override
    public Enum<?> getViewEnumValue(String enumValue) {
        return viewEnumMap.get(enumValue);
    }

    @Override
    public void showDefaultView(final Callback<Boolean> onReadyCallback) {
        HistoryManager.setLogNavigationHistory(false);
        //turn of history support for default showing until view is ready
        if (defaultView != null) {
            showView(defaultView, onReadyCallback);
        } else if (!viewMap.isEmpty()) {
            if (defaultView == null) {
                showView(viewMap.entrySet().iterator().next().getKey(), onReadyCallback);
            }
        }

    }

    /**
     * Show the view that was the first one added and will likely be the first one in a layout's menu, for
     * example.  Note that this is different than show default view.
     * 
     * @param onReadyCallback
     */
    public void showFirstView(Callback<Boolean> onReadyCallback) {
        HistoryManager.setLogNavigationHistory(false);
        if (!viewMap.isEmpty()) {
            showView(viewMap.entrySet().iterator().next().getKey(), onReadyCallback);
        } else {
            showDefaultView(onReadyCallback);
        }
    }

    /**
      * Check to see if current/all section(s) is valid (ie. does not contain any errors) - also displays
      * them in the ui if possible
      *
     * @param validationResults List of validation results for the layouts model.
     * @param checkCurrentSectionOnly true if errors should be checked on current section only, false if all sections should be checked
     * @return true if the specified sections (all or current) has any validation errors
     */
    public boolean isValid(List<ValidationResultInfo> validationResults, boolean checkCurrentSectionOnly) {
        return isValid(validationResults, checkCurrentSectionOnly, true);
    }

    /**
     * @see LayoutController#isValid(List, boolean)
     * @param validationResults
     * @param checkCurrentSectionOnly
     * @param allFields
     * @return
     */
    public boolean isValid(List<ValidationResultInfo> validationResults, boolean checkCurrentSectionOnly,
            boolean allFields) {

        boolean isValid = true;

        clearAllWarnings();

        if (checkCurrentSectionOnly) {
            //Check for validation errors on the currently displayed section only
            View v = getCurrentView();
            if (v instanceof Section) {
                isValid = isValid(validationResults, (Section) v, allFields);
            }
            if (this.isStartViewShowing()) {
                if (startPopupView instanceof Section) {
                    isValid = isValid(validationResults, ((Section) startPopupView), allFields) && isValid;
                }
            }
        } else {
            //Check for validation errors on all sections
            String errorSections = "";
            StringBuilder errorSectionsbuffer = new StringBuilder();
            errorSectionsbuffer.append(errorSections);
            for (Entry<Enum<?>, View> entry : viewMap.entrySet()) {
                View v = entry.getValue();
                if (v instanceof Section) {
                    if (!isValid(validationResults, (Section) v, allFields)) {
                        isValid = false;
                        errorSectionsbuffer.append(((SectionView) v).getName() + ", ");
                    }
                }
            }
            if (this.isStartViewShowing()) {
                if (startPopupView instanceof Section) {
                    isValid = isValid(validationResults, ((Section) startPopupView), allFields) && isValid;
                }
            }
            errorSections = errorSectionsbuffer.toString();
            if (!errorSections.isEmpty()) {
                errorSections = errorSections.substring(0, errorSections.length() - 2);
                //container.addMessage("Following section(s) has errors & must be corrected: " + errorSections);
            }
        }

        return isValid;
    }

    private boolean isValid(List<ValidationResultInfo> validationResults, Section section, boolean allFields) {
        ErrorLevel status;
        if (allFields) {
            section.setFieldHasHadFocusFlags(true);
            status = section.processValidationResults(validationResults);
        } else {
            status = section.processValidationResults(validationResults, false);
        }

        return (status != ErrorLevel.ERROR);
    }

    /**
     * This clears all warnings that currently displayed on all fields and sections.
     */
    protected void clearAllWarnings() {
        for (Entry<Enum<?>, View> entry : viewMap.entrySet()) {
            View v = entry.getValue();
            if (v instanceof Section) {
                ((Section) v).clearValidationWarnings();
            }
        }
    }

    /**
     * This particular implementation of beforeViewChange checks to see if all its view contains a Controller
     * and if it does checks with that controller to see if it is ok to change the view.  OkToChange callback
     * will be exec with true if the view is allowed to be changed at this time.  This method can be overriden
     * to provide additional functionality to stop a view from being changed when there is some additional
     * processing that needs to occur in the ui before the view changes.
     * 
     * @see org.kuali.student.common.ui.client.mvc.Controller#beforeViewChange(java.lang.Enum, org.kuali.student.common.ui.client.mvc.Callback)
     */
    @Override
    public void beforeViewChange(Enum<?> viewChangingTo, Callback<Boolean> okToChange) {

        if (this.getCurrentView() instanceof Controller) {
            ((Controller) this.getCurrentView()).beforeViewChange(viewChangingTo, okToChange);
        } else {
            okToChange.exec(true);
        }

        this.showExport(isExportButtonActive());
    }

    /**
     * Shows warnings stored to the application context
     * (i.e: dark-yellow highlighting of conflicts during review of a Course Proposal)
     */
    protected void showWarnings() {
        if (!Application.getApplicationContext().getValidationWarnings().isEmpty()) {
            isValid(Application.getApplicationContext().getValidationWarnings(), true);
        }
    }

    @Override
    public Widget asWidget() {
        return this;
    }

    @Override
    public boolean beforeHide() {
        return true;
    }

    /**
     * Default implementation does nothing on before show.  Override to do other things before THIS view is
     * shown.
     * @see org.kuali.student.common.ui.client.mvc.View#beforeShow(org.kuali.student.common.ui.client.mvc.Callback)
     */
    @Override
    public void beforeShow(Callback<Boolean> onReadyCallback) {
        onReadyCallback.exec(true);
    }

    @Override
    public Controller getController() {
        return parentController;
    }

    @Override
    public String getName() {
        if (name == null && viewType != null) {
            return viewType.toString();
        } else {
            return name;
        }
    }

    @Override
    public Enum<?> getViewEnum() {
        return viewType;
    }

    public void setViewEnum(Enum<?> viewType) {
        this.viewType = viewType;
    }

    /**
     * Sets the name of this LayoutController.  This name is used in the breadcrumb and window's title.
     * Setting the name to the empty string will omit the breadcrumb - this is sometimes desired.
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public void setController(Controller controller) {
        parentController = controller;
    }

    @Override
    public void collectBreadcrumbNames(List<String> names) {
        names.add(this.getName());
        if (this.getCurrentView() != null) {
            this.getCurrentView().collectBreadcrumbNames(names);
        }
    }

    @Override
    public void clear() {

    }

    public boolean isExportButtonActive() {
        return false;
    }

    @Override
    public void showExport(boolean show) {
        // TODO Auto-generated method stub

    }
}