com.ocs.dynamo.ui.composite.layout.SimpleSearchLayout.java Source code

Java tutorial

Introduction

Here is the source code for com.ocs.dynamo.ui.composite.layout.SimpleSearchLayout.java

Source

/*
   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.ocs.dynamo.ui.composite.layout;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Lists;
import com.ocs.dynamo.constants.DynamoConstants;
import com.ocs.dynamo.dao.query.FetchJoinInformation;
import com.ocs.dynamo.domain.AbstractEntity;
import com.ocs.dynamo.domain.model.AttributeModel;
import com.ocs.dynamo.domain.model.EntityModel;
import com.ocs.dynamo.service.BaseService;
import com.ocs.dynamo.ui.component.DefaultVerticalLayout;
import com.ocs.dynamo.ui.composite.form.FormOptions;
import com.ocs.dynamo.ui.composite.form.ModelBasedEditForm;
import com.ocs.dynamo.ui.composite.form.ModelBasedSearchForm;
import com.ocs.dynamo.ui.composite.table.ServiceResultsTableWrapper;
import com.ocs.dynamo.ui.composite.type.ScreenMode;
import com.ocs.dynamo.ui.container.QueryType;
import com.vaadin.data.Container.Filter;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.sort.SortOrder;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.Field;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

/**
 * A simple page that contains a search form and a table with search results
 * 
 * @author bas.rutten
 * @param <ID>
 *            type of the primary key
 * @param <T>
 *            type of the entity
 */
@SuppressWarnings("serial")
public class SimpleSearchLayout<ID extends Serializable, T extends AbstractEntity<ID>>
        extends BaseCollectionLayout<ID, T> {

    private static final long serialVersionUID = 4606800218149558500L;

    // button for adding new items. displayed by default
    private Button addButton;

    // any additional filter that are always added to the query
    private List<Filter> additionalFilters;

    // the fetch joins to use when fetching an item for display in the detail
    // screen
    private FetchJoinInformation[] detailJoins;

    // button for opening the screen in edit mode
    private Button editButton;

    // the edit form
    private ModelBasedEditForm<ID, T> editForm;

    // label
    private Label noSearchYetLabel;

    // map of extra filters to be applied to certain fields
    private Map<String, Filter> fieldFilters;

    // the main layout (in edit mode)
    private VerticalLayout mainEditLayout;

    // the main layout (in search mode)
    private VerticalLayout mainSearchLayout;

    private VerticalLayout searchResultsLayout;

    // the number of columns in the search form
    private int nrOfColumns = 1;

    // the query type (paging or id-based)
    private QueryType queryType;

    // the button that is used to remove an item - disabled by default, must be
    // explicitly set to visible
    private Button removeButton;

    // the search form
    private ModelBasedSearchForm<ID, T> searchForm;

    // the set of currently selected items
    private Collection<T> selectedItems;

    private boolean searchLayoutConstructed;

    /**
     * Constructor - all fields
     * 
     * @param service
     *            the service that is used to query the database
     * @param entityModel
     *            the entity model of the entities to search for
     * @param queryType
     *            the type of the query
     * @param formOptions
     *            form options that governs which buttons and options to show
     * @param fieldFilters
     *            filters that are applied to individual search fields
     * @param additionalFilters
     *            search filters that are added to every query
     * @param sortOrder
     *            the default sort order
     * @param joins
     *            the joins to include in the query
     */
    public SimpleSearchLayout(BaseService<ID, T> service, EntityModel<T> entityModel, QueryType queryType,
            FormOptions formOptions, Map<String, Filter> fieldFilters, List<Filter> additionalFilters,
            SortOrder sortOrder, FetchJoinInformation... joins) {
        super(service, entityModel, formOptions, sortOrder, joins);
        this.queryType = queryType;
        this.additionalFilters = additionalFilters;
        this.fieldFilters = fieldFilters;
    }

    /**
     * Constructor - only the most important fields
     * 
     * @param service
     *            the service that is used to query the database
     * @param entityModel
     *            the entity model of the entities to search for
     * @param queryType
     *            the type of the query
     * @param formOptions
     *            form options that governs which buttons and options to show
     * @param sortOrder
     *            the default sort order
     * @param joins
     *            the joins to include in the query
     */
    public SimpleSearchLayout(BaseService<ID, T> service, EntityModel<T> entityModel, QueryType queryType,
            FormOptions formOptions, SortOrder sortOrder, FetchJoinInformation... joins) {
        super(service, entityModel, formOptions, sortOrder, joins);
        this.queryType = queryType;
    }

    /**
     * Responds to the toggling of the visibility of the search fields
     * 
     * @param visible
     *            whether the search fields are now visible
     */
    protected void afterSearchFieldToggle(boolean visible) {
        // overwrite in subclasses
    }

    @Override
    public void attach() {
        super.attach();
        build();
    }

    /**
     * Lazily constructs the screen
     */
    @Override
    public void build() {
        if (mainSearchLayout == null) {
            mainSearchLayout = new DefaultVerticalLayout();

            if (getFormOptions().isSearchImmediately()) {
                constructSearchLayout();
                searchLayoutConstructed = true;
            }

            mainSearchLayout.addComponent(getSearchForm());

            searchResultsLayout = new DefaultVerticalLayout(false, false);
            mainSearchLayout.addComponent(searchResultsLayout);

            if (getFormOptions().isSearchImmediately()) {
                searchResultsLayout.addComponent(getTableWrapper());
            } else {
                // add a label
                noSearchYetLabel = new Label(message("ocs.no.search.yet"));
                searchResultsLayout.addComponent(noSearchYetLabel);

                // set up a click listener that will construct the table when needed
                getSearchForm().getSearchButton().addClickListener(new Button.ClickListener() {

                    @Override
                    public void buttonClick(ClickEvent event) {
                        getSearchForm().setSearchable(getTableWrapper());
                        search();
                    }
                });
            }

            // add button
            addButton = constructAddButton();
            if (addButton != null) {
                getButtonBar().addComponent(addButton);
            }

            // edit/view button
            editButton = constructEditButton();
            if (editButton != null) {
                registerButton(editButton);
                getButtonBar().addComponent(editButton);
            }

            // remove button
            removeButton = constructRemoveButton();
            if (removeButton != null) {
                registerButton(removeButton);
                getButtonBar().addComponent(removeButton);
            }

            // callback for adding additional buttons
            postProcessButtonBar(getButtonBar());
            mainSearchLayout.addComponent(getButtonBar());

            postProcessLayout(mainSearchLayout);

            if (getFormOptions().isSearchImmediately()) {
                search();
            }
        }
        setCompositionRoot(mainSearchLayout);
    }

    /**
     * Constructs the edit button - this will display the details view when clicked
     * 
     * @return
     */
    protected Button constructEditButton() {
        // edit button
        Button eb = new Button(getFormOptions().isOpenInViewMode() ? message("ocs.view") : message("ocs.edit"));
        eb.addClickListener(new Button.ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                if (getSelectedItem() != null) {
                    doEdit();
                }
            }
        });

        // show button if editing is allowed or if detail screen opens in view mode
        eb.setVisible(
                getFormOptions().isShowEditButton() && (isEditAllowed() || getFormOptions().isOpenInViewMode()));
        return eb;
    }

    /**
     * Constructs the remove button
     * 
     * @return
     */
    protected Button constructRemoveButton() {
        Button rb = new RemoveButton() {

            @Override
            protected void doDelete() {
                remove();
            }

        };
        rb.setVisible(isEditAllowed() && getFormOptions().isShowRemoveButton());
        return rb;
    }

    public void constructSearchLayout() {
        // construct table and set properties
        getTableWrapper().getTable().setPageLength(getPageLength());
        getTableWrapper().getTable().setSortEnabled(isSortEnabled());
        getTableWrapper().getTable().setMultiSelect(isMultiSelect());

        // add a listener to respond to the selection of an item
        getTableWrapper().getTable().addValueChangeListener(new Property.ValueChangeListener() {
            @Override
            public void valueChange(ValueChangeEvent event) {
                select(getTableWrapper().getTable().getValue());
                checkButtonState(getSelectedItem());
            }
        });

        // double click listener
        if (getFormOptions().isShowEditButton()) {
            getTableWrapper().getTable().addItemClickListener(new ItemClickListener() {

                @Override
                public void itemClick(ItemClickEvent event) {
                    if (event.isDoubleClick()) {
                        select(event.getItem().getItemProperty(DynamoConstants.ID).getValue());
                        doEdit();
                    }
                }
            });
        }

        // table dividers
        constructTableDividers();
    }

    /**
     * Lazily constructs the search form
     * 
     * @return
     */
    protected ModelBasedSearchForm<ID, T> constructSearchForm() {
        ModelBasedSearchForm<ID, T> result = new ModelBasedSearchForm<ID, T>(
                getFormOptions().isSearchImmediately() ? getTableWrapper() : null, getEntityModel(),
                getFormOptions(), this.additionalFilters, this.fieldFilters) {

            @Override
            protected void afterSearchFieldToggle(boolean visible) {
                SimpleSearchLayout.this.afterSearchFieldToggle(visible);
            }

            @Override
            protected Field<?> constructCustomField(EntityModel<T> entityModel, AttributeModel attributeModel) {
                return SimpleSearchLayout.this.constructCustomField(entityModel, attributeModel, false, true);
            }
        };
        result.setNrOfColumns(getNrOfColumns());
        result.setFieldEntityModels(getFieldEntityModels());
        result.build();

        return result;
    }

    /**
     * Lazily constructs the table wrapper
     */
    @Override
    public ServiceResultsTableWrapper<ID, T> constructTableWrapper() {
        ServiceResultsTableWrapper<ID, T> result = new ServiceResultsTableWrapper<ID, T>(this.getService(),
                getEntityModel(), getQueryType(), null, getSortOrders(), getJoins());
        result.build();
        return result;
    }

    /**
     * Opens a custom detail view
     * 
     * @param component
     */
    protected void customDetailView(Component component) {
        setCompositionRoot(component);
    }

    /**
     * Open the screen in details-mode
     * 
     * @param entity
     *            the entity to display
     */
    protected void detailsMode(T entity) {
        if (mainEditLayout == null) {
            mainEditLayout = new DefaultVerticalLayout();
            mainEditLayout.setStyleName(DynamoConstants.CSS_CLASS_HALFSCREEN);

            // set the form options for the detail form
            FormOptions options = new FormOptions();
            options.setOpenInViewMode(getFormOptions().isOpenInViewMode());
            options.setScreenMode(ScreenMode.VERTICAL);

            if (options.isOpenInViewMode()) {
                options.setShowBackButton(true);
                options.setShowEditButton(true);
            }

            editForm = new ModelBasedEditForm<ID, T>(entity, getService(), getEntityModel(), options,
                    fieldFilters) {

                @Override
                protected void afterEditDone(boolean cancel, boolean newObject, T entity) {
                    if (getFormOptions().isOpenInViewMode()) {
                        if (newObject) {
                            back();
                        } else {
                            // if details screen opens in view mode, simply switch to view mode
                            setViewMode(true);
                            detailsMode(entity);
                        }
                    } else {
                        // otherwise go back to the main screen
                        back();
                    }
                }

                @Override
                protected void afterModeChanged(boolean viewMode) {
                    SimpleSearchLayout.this.afterModeChanged(viewMode, editForm);
                }

                @Override
                protected void back() {
                    searchMode();
                }

                @Override
                protected Field<?> constructCustomField(EntityModel<T> entityModel, AttributeModel attributeModel,
                        boolean viewMode) {
                    return SimpleSearchLayout.this.constructCustomField(entityModel, attributeModel, true, false);
                }

                @Override
                protected boolean isEditAllowed() {
                    return SimpleSearchLayout.this.isEditAllowed();
                }

                @Override
                protected void postProcessButtonBar(HorizontalLayout buttonBar, boolean viewMode) {
                    SimpleSearchLayout.this.postProcessDetailButtonBar(buttonBar, viewMode);
                }

                @Override
                protected void postProcessEditFields() {
                    SimpleSearchLayout.this.postProcessEditFields(editForm);
                }

            };
            editForm.setFieldEntityModels(getFieldEntityModels());
            editForm.build();
            mainEditLayout.addComponent(editForm);
        } else {
            editForm.setViewMode(getFormOptions().isOpenInViewMode());
            editForm.setEntity(entity);
        }

        checkButtonState(getSelectedItem());
        afterDetailSelected(editForm, entity);
        setCompositionRoot(mainEditLayout);
    }

    /**
     * Callback method that is called when the user presses the edit method. Will by default open
     * the screen in edit mode. Overwrite in subclass if needed
     */
    protected void doEdit() {
        detailsMode(getSelectedItem());
    }

    /**
     * Performs the actual remove functionality - overwrite in subclass if needed
     */
    protected void doRemove() {
        getService().delete(getSelectedItem());
    }

    public Button getAddButton() {
        return addButton;
    }

    protected List<Filter> getAdditionalFilters() {
        return additionalFilters;
    }

    public FetchJoinInformation[] getDetailJoins() {
        return detailJoins;
    }

    public Button getEditButton() {
        return editButton;
    }

    protected Map<String, Filter> getFieldFilters() {
        return fieldFilters;
    }

    public int getNrOfColumns() {
        return nrOfColumns;
    }

    public QueryType getQueryType() {
        return queryType;
    }

    public Button getRemoveButton() {
        return removeButton;
    }

    public ModelBasedSearchForm<ID, T> getSearchForm() {
        if (searchForm == null) {
            searchForm = constructSearchForm();
        }
        return searchForm;
    }

    public Collection<T> getSelectedItems() {
        return selectedItems;
    }

    /**
     * Reloads the details view only
     */
    public void reloadDetails() {
        this.setSelectedItem(getService().fetchById(this.getSelectedItem().getId(), getJoins()));
        detailsMode(getSelectedItem());
    }

    /**
     * Performs the actual delete action
     */
    protected final void remove() {
        doRemove();
        // refresh the results so that the deleted item is no longer
        // there
        setSelectedItem(null);
        checkButtonState(getSelectedItem());
        search();
    }

    public void replaceLabel(String propertyName) {
        if (editForm != null) {
            editForm.replaceLabel(propertyName);
        }
    }

    /**
     * Perform the actual search
     */
    public void search() {

        // lazily construct search form if it is not there yet
        if (!searchLayoutConstructed) {
            constructSearchLayout();
            searchResultsLayout.addComponent(getTableWrapper());
            getSearchForm().setSearchable(getTableWrapper());
            searchResultsLayout.removeComponent(noSearchYetLabel);
            searchLayoutConstructed = true;
        }

        if (getSearchForm().getCompositeFilter() != null) {
            // search without clearing any previous filters
            getSearchForm().searchImmediately();
        } else {
            // build filter and search
            getSearchForm().search();
        }

        getTableWrapper().getTable().select(null);
        setSelectedItem(null);
        checkButtonState(getSelectedItem());
    }

    /**
     * Puts the screen in search mode
     */
    protected void searchMode() {
        setCompositionRoot(mainSearchLayout);
        getSearchForm().refresh();
        search();
    }

    /**
     * Select one or more items
     * 
     * @param selectedItems
     *            the item or items to select
     */
    @SuppressWarnings("unchecked")
    public void select(Object selectedItems) {
        if (selectedItems != null) {
            if (selectedItems instanceof Collection<?>) {
                // the lazy query container returns an array of IDs of the
                // selected items

                Collection<?> col = (Collection<?>) selectedItems;
                if (col.size() == 1) {
                    ID id = (ID) col.iterator().next();
                    setSelectedItem(getService().fetchById(id, getDetailJoins()));
                    this.selectedItems = Lists.newArrayList(getSelectedItem());
                } else if (col.size() > 1) {
                    // deal with the selection of multiple items
                    List<ID> ids = Lists.newArrayList();
                    for (Object c : col) {
                        ids.add((ID) c);
                    }
                    this.selectedItems = getService().fetchByIds(ids, getDetailJoins());
                }
            } else {
                // single item has been selected
                ID id = (ID) selectedItems;
                setSelectedItem(getService().fetchById(id, getDetailJoins()));
            }
        } else {
            setSelectedItem(null);
        }
    }

    public void setAdditionalFilters(List<Filter> additionalFilters) {
        this.additionalFilters = additionalFilters;
    }

    public void setDetailJoins(FetchJoinInformation[] detailJoins) {
        this.detailJoins = detailJoins;
    }

    public void setFieldFilters(Map<String, Filter> fieldFilters) {
        this.fieldFilters = fieldFilters;
    }

    public void setNrOfColumns(int nrOfColumns) {
        this.nrOfColumns = nrOfColumns;
    }

    public void setQueryType(QueryType queryType) {
        this.queryType = queryType;
    }

    public Filter getCompositeFilter() {
        return getSearchForm().getCompositeFilter();
    }

    public void setCompositeFilter(Filter filter) {
        getSearchForm().setCompositeFilter(filter);
    }
}