com.ocs.dynamo.ui.composite.form.AbstractModelBasedSearchForm.java Source code

Java tutorial

Introduction

Here is the source code for com.ocs.dynamo.ui.composite.form.AbstractModelBasedSearchForm.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.form;

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

import com.ocs.dynamo.domain.AbstractEntity;
import com.ocs.dynamo.domain.model.EntityModel;
import com.ocs.dynamo.domain.model.impl.ModelBasedFieldFactory;
import com.ocs.dynamo.filter.listener.FilterChangeEvent;
import com.ocs.dynamo.filter.listener.FilterListener;
import com.ocs.dynamo.ui.Searchable;
import com.ocs.dynamo.ui.component.DefaultHorizontalLayout;
import com.ocs.dynamo.ui.component.DefaultVerticalLayout;
import com.ocs.dynamo.ui.utils.VaadinUtils;
import com.vaadin.data.Container.Filter;
import com.vaadin.data.util.filter.And;
import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
import com.vaadin.event.ShortcutAction;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Layout;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;

/**
 * An abstract model search form that servers as the basis for other model based search forms
 * 
 * @author bas.rutten
 *
 * @param <ID>
 *            the type of the ID of the entity
 * @param <T>
 *            the type of the entity
 */
public abstract class AbstractModelBasedSearchForm<ID extends Serializable, T extends AbstractEntity<ID>>
        extends AbstractModelBasedForm<ID, T> implements FilterListener, Button.ClickListener {

    private static final long serialVersionUID = 2146875385041665280L;

    /**
     * Any filters that will always be applied to any search query (use these to restrict the result
     * set beforehand)
     */
    private List<Filter> additionalFilters = new ArrayList<>();

    /**
     * Button to clear the search form
     */
    private Button clearButton;

    /**
     * The filter that is created by taking all individual field filters together
     */
    private Filter compositeFilter;

    /**
     * The list of currently active search filters
     */
    private List<Filter> currentFilters = new ArrayList<Filter>();

    /**
     * Field factory used for constructing search fields
     */
    private ModelBasedFieldFactory<T> fieldFactory;

    /**
     * The layout that holds the various filters
     */
    private Layout filterLayout;

    /**
     * The object that will be searched when the user presses the "Search" button
     */
    private Searchable searchable;

    /**
     * The "search" button
     */
    private Button searchButton;

    /**
     * The toggle button (hides/shows the search form)
     */
    private Button toggleButton;

    /**
     * The panel that wraps around the filter form
     */
    private Panel wrapperPanel;

    /**
     * 
     * @param searchable
     * @param entityModel
     * @param formOptions
     * @param additionalFilters
     * @param fieldFilters
     */
    public AbstractModelBasedSearchForm(Searchable searchable, EntityModel<T> entityModel, FormOptions formOptions,
            List<Filter> additionalFilters, Map<String, Filter> fieldFilters) {
        super(formOptions, fieldFilters, entityModel);
        this.fieldFactory = ModelBasedFieldFactory.getSearchInstance(entityModel, getMessageService());
        this.additionalFilters = additionalFilters == null ? new ArrayList<Filter>() : additionalFilters;
        this.currentFilters.addAll(this.additionalFilters);
        this.searchable = searchable;
    }

    /**
     * Callback method that is called when the user toggles the visibility of the search form
     * 
     * @param visible
     *            indicates if the search fields are visible now
     */
    protected void afterSearchFieldToggle(boolean visible) {
        // override in subclasses
    }

    @Override
    public void build() {
        VerticalLayout main = new DefaultVerticalLayout(false, true);

        // add a wrapper for adding an action handler
        wrapperPanel = new Panel();
        main.addComponent(wrapperPanel);

        // create the search form
        filterLayout = constructFilterLayout();
        wrapperPanel.setContent(filterLayout);

        // action handler for carrying out a search after an Enter press
        wrapperPanel.addActionHandler(new Handler() {

            private static final long serialVersionUID = -2136828212405809213L;

            private Action enter = new ShortcutAction(null, ShortcutAction.KeyCode.ENTER, null);

            @Override
            public Action[] getActions(Object target, Object sender) {
                return new Action[] { enter };
            }

            @Override
            public void handleAction(Action action, Object sender, Object target) {
                if (action == enter) {
                    search();
                }
            }
        });

        // create the button bar
        HorizontalLayout buttonBar = new DefaultHorizontalLayout();
        main.addComponent(buttonBar);

        constructButtonBar(buttonBar);

        // add custom buttons
        postProcessButtonBar(buttonBar);

        // add any custom functionality
        postProcessLayout();

        // initial search
        // search();

        setCompositionRoot(main);
    }

    @Override
    public void buttonClick(ClickEvent event) {
        if (event.getButton() == searchButton) {
            search();
        } else if (event.getButton() == clearButton) {
            if (getFormOptions().isConfirmClear()) {
                VaadinUtils.showConfirmDialog(getMessageService(), message("ocs.confirm.clear"), new Runnable() {

                    @Override
                    public void run() {
                        clear();
                        search();
                    }
                });
            } else {
                clear();
                search();
            }
        } else if (event.getButton() == toggleButton) {
            toggle(!wrapperPanel.isVisible());
        }
    }

    /**
     * Clears the search filters
     */
    protected void clear() {
        currentFilters.clear();
        currentFilters.addAll(getAdditionalFilters());
    }

    /**
     * Creates buttons and adds them to the button bar
     * 
     * @param buttonBar
     */
    protected abstract void constructButtonBar(Layout buttonBar);

    /**
     * Constructs the "clear" button
     * 
     * @return
     */
    protected Button constructClearButton() {
        clearButton = new Button(message("ocs.clear"));
        clearButton.setImmediate(true);
        clearButton.addClickListener(this);
        clearButton.setVisible(!getFormOptions().isHideClearButton());
        return clearButton;
    }

    /**
     * Constructs the layout that holds all the filter components
     * 
     * @return
     */
    protected abstract Layout constructFilterLayout();

    /**
     * Constructs the "search" button
     * 
     * @return
     */
    protected Button constructSearchButton() {
        searchButton = new Button(message("ocs.search"));
        searchButton.setImmediate(true);
        searchButton.addClickListener(this);
        return searchButton;
    }

    /**
     * Constructs the "toggle" button
     * 
     * @return
     */
    protected Button constructToggleButton() {
        toggleButton = new Button(message("ocs.hide"));
        toggleButton.addClickListener(this);
        toggleButton.setVisible(getFormOptions().isShowToggleButton());
        return toggleButton;
    }

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

    public Button getClearButton() {
        return clearButton;
    }

    public Filter getCompositeFilter() {
        return compositeFilter;
    }

    public List<Filter> getCurrentFilters() {
        return currentFilters;
    }

    public ModelBasedFieldFactory<T> getFieldFactory() {
        return fieldFactory;
    }

    public Layout getFilterLayout() {
        return filterLayout;
    }

    public Searchable getSearchable() {
        return searchable;
    }

    public Button getSearchButton() {
        return searchButton;
    }

    /**
     * Responds to a filter change
     */
    @Override
    public void onFilterChange(FilterChangeEvent event) {
        currentFilters.remove(event.getOldFilter());
        if (event.getNewFilter() != null) {
            currentFilters.add(event.getNewFilter());
        }
    }

    /**
     * Callback method that allows the user to modify the button bar
     * 
     * @param groups
     */
    protected void postProcessButtonBar(Layout buttonBar) {
        // Use in subclass to add additional buttons
    }

    /**
     * Perform any actions necessary after the layout has been build
     */
    protected void postProcessLayout() {

    }

    /**
     * Trigger the actual search
     */
    public void search() {
        if (!currentFilters.isEmpty()) {
            compositeFilter = new And(currentFilters.toArray(new Filter[0]));
        } else {
            // search without any filters
            compositeFilter = null;
        }

        if (searchable != null) {
            searchable.search(compositeFilter);
        }
    }

    /**
     * Search immediately without reconstructing the filter
     */
    public void searchImmediately() {
        if (searchable != null) {
            searchable.search(compositeFilter);
        }
    }

    public void setCompositeFilter(Filter compositeFilter) {
        this.compositeFilter = compositeFilter;
        if (searchable != null) {
            searchable.search(compositeFilter);
        }
    }

    /**
     * Toggles the visibility of the search form
     * 
     * @param show
     *            whether to show or hide the form
     */
    protected void toggle(boolean show) {
        if (!show) {
            toggleButton.setCaption(message("ocs.show.search.fields"));
        } else {
            toggleButton.setCaption(message("ocs.hide.search.fields"));
        }
        wrapperPanel.setVisible(show);
        afterSearchFieldToggle(wrapperPanel.isVisible());
    }

    public void setSearchable(Searchable searchable) {
        this.searchable = searchable;
    }

}