org.nuclos.client.ui.collect.result.SearchResultStrategy.java Source code

Java tutorial

Introduction

Here is the source code for org.nuclos.client.ui.collect.result.SearchResultStrategy.java

Source

//Copyright (C) 2011  Novabit Informationssysteme GmbH
//
//This file is part of Nuclos.
//
//Nuclos is free software: you can redistribute it and/or modify
//it under the terms of the GNU Affero General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//Nuclos 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 Affero General Public License for more details.
//
//You should have received a copy of the GNU Affero General Public License
//along with Nuclos.  If not, see <http://www.gnu.org/licenses/>.
package org.nuclos.client.ui.collect.result;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Observer;

import javax.swing.JScrollBar;
import javax.swing.JViewport;

import org.apache.commons.lang.NullArgumentException;
import org.apache.log4j.Logger;
import org.nuclos.client.ui.CommonClientWorkerAdapter;
import org.nuclos.client.ui.CommonMultiThreader;
import org.nuclos.client.ui.Errors;
import org.nuclos.client.ui.UIUtils;
import org.nuclos.client.ui.collect.CollectController;
import org.nuclos.client.ui.collect.CollectState;
import org.nuclos.client.ui.collect.search.SearchWorker;
import org.nuclos.client.ui.message.MessageExchange;
import org.nuclos.client.ui.message.MessageExchange.MessageExchangeListener;
import org.nuclos.common.collect.collectable.Collectable;
import org.nuclos.common.collect.collectable.CollectableField;
import org.nuclos.common.collect.collectable.CollectableValueField;
import org.nuclos.common.collect.collectable.searchcondition.AtomicCollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableIdListCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableLikeCondition;
import org.nuclos.common.collect.collectable.searchcondition.CollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.SearchConditionUtils;
import org.nuclos.common.collect.exception.CollectableFieldFormatException;
import org.nuclos.common.collection.Pair;
import org.nuclos.common2.CommonRunnable;
import org.nuclos.common2.PreferencesUtils;
import org.nuclos.common2.exception.CommonBusinessException;
import org.nuclos.common2.exception.CommonValidationException;
import org.nuclos.common2.exception.PreferencesException;

public class SearchResultStrategy<Clct extends Collectable> implements ISearchResultStrategy<Clct> {

    private static final Logger LOG = Logger.getLogger(SearchResultStrategy.class);

    private ResultController<Clct> rc;

    public SearchResultStrategy() {
    }

    public void setResultController(ResultController<Clct> rc) {
        this.rc = rc;
    }

    private final CollectController<Clct> getCollectController() {
        return rc.getCollectController();
    }

    // search stuff

    /**
     * @deprecated Use multithreaded search for new applications.
     */
    public void refreshResult() throws CommonBusinessException {
        getCollectController().getSearchStrategy().search(true);
    }

    /**
     * Command: refresh search result.
     * Repeats the current search.
     */
    public final void cmdRefreshResult() {
        this.cmdSearch(true);
    }

    public final void cmdRefreshResult(List<Observer> lstObservers) {
        this.cmdObservableMultiThreadingSearch(lstObservers);
    }

    /**
     * Command: search.
     * Common implementation for cmdSearch() and cmdRefreshResult().
     * @param bRefreshOnly Refresh only? (false: perform a new search)
     */
    private void cmdSearch(boolean bRefreshOnly) {
        final CollectController<Clct> cc = getCollectController();
        LOG.debug("START cmdSearch");
        // save search search terms for autocompletion
        saveSearchTerms();
        // TODO call getSearchWorker(bRefreshOnly)
        final SearchWorker<Clct> searchWorker = cc.getSearchStrategy().getSearchWorker();
        if (cc.isMultiThreadingEnabled() && (searchWorker != null)) {
            this.cmdSearchMultiThreaded(searchWorker, bRefreshOnly);
        } else {
            this.cmdSearchSingleThreaded(bRefreshOnly);
        }
        LOG.debug("FINISHED cmdSearch");
    }

    /**
     * Command: search.
     * Observable implementation for cmdSearch() and cmdRefreshResult().
     * @param lstObservers "search finished" Observers
     */
    private void cmdObservableMultiThreadingSearch(List<Observer> lstObservers) {
        LOG.debug("START cmdObservableMultiThreadingSearch");
        final SearchWorker<Clct> searchWorker = getCollectController().getSearchStrategy()
                .getSearchWorker(lstObservers);
        if (searchWorker != null) {
            this.cmdSearchMultiThreaded(searchWorker, true);
        }
        LOG.debug("FINISHED cmdObservableMultiThreadingSearch");
    }

    /**
     * Command: search.
     * Performs a search, according to the current search condition, if any.
     */
    public final void cmdSearch() {
        // reset CollectableIdListCondition
        getCollectController().getSearchStrategy().setCollectableIdListCondition(null);
        this.cmdSearch(false);
    }

    /**
     * @param searchworker
     * @precondition searchworker != null
     */
    private void cmdSearchMultiThreaded(final SearchWorker<Clct> searchworker, final boolean bRefreshOnly) {
        final CollectController<Clct> cc = getCollectController();
        UIUtils.runShortCommand(cc.getTab(), new CommonRunnable() {
            @Override
            public void run() throws CommonValidationException {
                if (searchworker == null) {
                    throw new NullArgumentException("searchworker");
                }

                if (!cc.stopEditingInSearch()) {
                    throw new CommonValidationException(
                            "Die eingegebene Suchbedingung ist ung\u00fcltig bzw. unvollst\u00e4ndig.");
                } else {
                    // TODO remove - painting isn't necessary here:
                    UIUtils.paintImmediately(cc.getSearchPanel().tfStatusBar);
                    //rc.writeSelectedFieldsAndWidthsToPreferences();
                    final List<Clct> selected = cc.getSelectedCollectables();
                    adjustVerticalScrollBarForSearch(bRefreshOnly);

                    CommonMultiThreader.getInstance().execute(new CommonClientWorkerAdapter<Clct>(cc) {
                        private volatile List<Clct> lstclctResult;

                        @Override
                        public void init() throws CommonBusinessException {
                            super.init();

                            searchworker.startSearch();
                        }

                        @Override
                        public void work() throws CommonBusinessException {
                            this.lstclctResult = searchworker.getResult();
                        }

                        @Override
                        public void paint() throws CommonBusinessException {
                            if (this.lstclctResult != null) {
                                searchworker.finishSearch(this.lstclctResult);

                                if (cc.isSearchPanelAvailable()) {
                                    // TODO On refresh, it's "counter intuitive" to leave result mode here.
                                    cc.setCollectState(CollectState.OUTERSTATE_SEARCH,
                                            CollectState.SEARCHMODE_SYNCHED);
                                }
                                if (selected != null && !selected.isEmpty()) {
                                    selected.clear();
                                }
                                cc.setCollectState(CollectState.OUTERSTATE_RESULT,
                                        CollectState.RESULTMODE_NOSELECTION);
                            }

                            super.paint();
                            if (cc.getResultPanel() != null) {
                                cc.getResultPanel().requestFocusInWindow();
                            }
                        }
                    });
                }
            }
        });
    }

    /**
     * Command: search.
     * Performs a search, according to the current search condition, if any.
     * @param bRefreshOnly Refresh only? (false: perform a new search)
     * @deprecated always search multi threaded
     */
    @Deprecated
    private void cmdSearchSingleThreaded(final boolean bRefreshOnly) {
        final CollectController<Clct> cc = getCollectController();
        UIUtils.runCommand(cc.getTab(), new Runnable() {
            @Override
            public void run() {
                try {
                    if (!cc.stopEditingInSearch()) {
                        throw new CommonValidationException(
                                "Die eingegebene Suchbedingung ist ung\u00fcltig bzw. unvollst\u00e4ndig.");
                    } else {
                        // update the status bar before performing the search:
                        UIUtils.paintImmediately(cc.getSearchPanel().tfStatusBar);

                        // Write the column widths to preferences, so they can be restored after searching is finished
                        //                  rc.writeSelectedFieldsAndWidthsToPreferences();
                        adjustVerticalScrollBarForSearch(bRefreshOnly);
                        cc.getSearchStrategy().search(bRefreshOnly);

                        if (cc.isSearchPanelAvailable()) {
                            // TODO On refresh, it's "counter intuitive" to leave result mode here.
                            cc.setCollectState(CollectState.OUTERSTATE_SEARCH, CollectState.SEARCHMODE_SYNCHED);
                        }
                        cc.setCollectState(CollectState.OUTERSTATE_RESULT, CollectState.RESULTMODE_NOSELECTION);

                        // Searching is finished, the result columns have been replaced in the model, so retore the previous widths
                        rc.setColumnWidths(cc.getResultTable());
                        if (cc.getResultPanel() != null) {
                            cc.getResultPanel().requestFocusInWindow();
                        }
                    }
                } catch (/* CommonBusiness */ Exception ex) {
                    Errors.getInstance().showExceptionDialog(cc.getTab(), ex);
                }
            }
        });
    }

    private void saveSearchTerms() {
        final CollectController<Clct> cc = getCollectController();
        try {
            CollectableSearchCondition cond = cc.getSearchStrategy().getCollectableSearchCondition();
            Map<String, CollectableField> m = SearchConditionUtils.getAtomicFieldsMap(cond);

            if (cond != null) {
                if (cond instanceof AtomicCollectableSearchCondition) {
                    if (cond instanceof CollectableLikeCondition) {
                        m.put(((CollectableLikeCondition) cond).getFieldName(),
                                new CollectableValueField(((CollectableLikeCondition) cond).getLikeComparand()));
                    }
                }

                for (String key : m.keySet()) {
                    ArrayList<String> l = PreferencesUtils.getStringList(cc.getPreferences().node("fields"), key);
                    CollectableField field = m.get(key);

                    if (!field.isNull() && field.getValue() != null) {
                        String s = field.getValue().toString();
                        if (l.contains(s))
                            l.remove(s);

                        l.add(0, s);
                    }
                    while (l.size() > 10)
                        l.remove(l.size() - 1);

                    PreferencesUtils.putStringList(cc.getPreferences().node("fields"), key, l);

                    MessageExchange.send(new Pair<String, String>(cc.getEntityName(), key),
                            MessageExchangeListener.ObjectType.TEXTFIELD,
                            MessageExchangeListener.MessageType.REFRESH);
                }
            }
        } catch (CollectableFieldFormatException e) {
            LOG.warn("saveSearchTerms failed: " + e, e);
        } catch (PreferencesException e) {
            LOG.warn("saveSearchTerms failed: " + e, e);
        }
    }

    private void adjustVerticalScrollBarForSearch(boolean bRefreshOnly) {
        final CollectController<Clct> cc = getCollectController();
        final JViewport viewport = cc.getResultPanel().getResultTableScrollPane().getViewport();
        if (bRefreshOnly) {
            final Rectangle rect = cc.getResultTable().getCellRect(0, 0, true);
            final Rectangle viewRect = viewport.getViewRect();
            // There seem to be different opinions about what scrollRectToVisible has to do at SUN and everywhere else...
            rect.setLocation(viewRect.x, viewRect.y);//rect.x - viewRect.x, rect.y - viewRect.y);
            viewport.scrollRectToVisible(rect);
        } else {
            Point viewPosition = viewport.getViewPosition();
            viewport.setViewPosition(new Point(viewPosition.x, 0));
        }
        final JScrollBar scrlbarVertical = cc.getResultPanel().getResultTableScrollPane().getVerticalScrollBar();
        scrlbarVertical.setValue(scrlbarVertical.getMinimum());
    }

    @Override
    public void viewList(List<? extends Object> ids) {
        getCollectController().getSearchStrategy()
                .setCollectableIdListCondition(new CollectableIdListCondition(ids));
        cmdSearch(false);
    }
}