/* *****************************************************************************
* JFire - it's hot - Free ERP System - http://jfire.org *
* Copyright (C) 2004-2005 NightLabs - http://NightLabs.org *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin St, Fifth Floor, *
* Boston, MA 02110-1301 USA *
* *
* Or get it online : *
* http://opensource.org/licenses/lgpl-license.php *
* *
* *
******************************************************************************/
package org.nightlabs.jfire.trade.ui.producttype.quicklist;
import java.util.Iterator;
import javax.jdo.FetchPlan;
import javax.jdo.JDOHelper;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.nightlabs.base.ui.job.Job;
import org.nightlabs.jdo.NLJDOHelper;
import org.nightlabs.jdo.query.QueryCollection;
import org.nightlabs.jfire.base.jdo.cache.Cache;
import org.nightlabs.jfire.base.login.ui.Login;
import org.nightlabs.jfire.idgenerator.IDGenerator;
import org.nightlabs.jfire.query.store.BaseQueryStore;
import org.nightlabs.jfire.query.store.QueryStore;
import org.nightlabs.jfire.query.store.dao.QueryStoreDAO;
import org.nightlabs.jfire.security.User;
import org.nightlabs.jfire.security.id.UserID;
import org.nightlabs.jfire.store.id.ProductTypeID;
import org.nightlabs.jfire.store.search.AbstractProductTypeGroupQuery;
import org.nightlabs.jfire.store.search.AbstractProductTypeQuery;
import org.nightlabs.jfire.store.search.VendorDependentQuery;
import org.nightlabs.jfire.trade.ArticleContainer;
import org.nightlabs.jfire.trade.ui.resource.Messages;
import org.nightlabs.progress.NullProgressMonitor;
import org.nightlabs.progress.ProgressMonitor;
import org.nightlabs.progress.SubProgressMonitor;
/**
* @author Alexander Bieber <alex[AT]nightlabs[DOT]de>
* @author Daniel Mazurek <daniel[AT]nightlabs[DOT]de>
*/
public abstract class AbstractProductTypeQuickListFilter
implements IProductTypeQuickListFilter
{
private static Logger logger = Logger.getLogger(AbstractProductTypeQuickListFilter.class);
public static String[] FETCH_GROUPS_QUERY_STORE = new String[] {
FetchPlan.DEFAULT,
BaseQueryStore.FETCH_GROUP_NAME,
BaseQueryStore.FETCH_GROUP_DESCRIPTION,
BaseQueryStore.FETCH_GROUP_SERIALISED_QUERIES
};
public static String[] FETCH_GROUPS_ARTICLE_CONTAINER_VENDOR = new String[] {
FetchPlan.DEFAULT,
ArticleContainer.FETCH_GROUP_VENDOR_ID,
};
private ListenerList selectionChangedListeners = new ListenerList();
private IStructuredSelection selection = StructuredSelection.EMPTY;
private QueryCollection<VendorDependentQuery> queryCollection;
private QueryStore defaultQueryStore;
private QuickListFilterQueryResultKey queryResultKey;
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener)
{
selectionChangedListeners.add(listener);
}
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener)
{
selectionChangedListeners.remove(listener);
}
/**
* Use this method to set the currently selected <tt>ProductTypeID</tt>. This
* method automatically notifies all interested listeners by firing a
* {@link SelectionChangedEvent}.
*
* @param productTypeID The new selected <tt>ProductTypeID</tt> or <tt>null</tt>.
*/
public void setSelectedProductTypeID(ProductTypeID productTypeID)
{
if (productTypeID == null)
this.selection = StructuredSelection.EMPTY;
else
this.selection = new StructuredSelection(productTypeID);
if (selectionChangedListeners.isEmpty())
return;
SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
for (int i=0; i<selectionChangedListeners.size(); i++) {
ISelectionChangedListener listener = (ISelectionChangedListener) selectionChangedListeners.getListeners()[i];
listener.selectionChanged(event);
}
}
/**
* Returns an instance of {@link IStructuredSelection} with exactly
* one instance of {@link ProductTypeID}.
*
* {@inheritDoc}
*/
@Override
public ISelection getSelection()
{
return selection;
}
@Override
public void setSelection(final ISelection selection)
{
if (getResultViewerControl() instanceof ISelectionProvider) {
final ISelectionProvider selectionProvider = (ISelectionProvider) getResultViewerControl();
if (selectionProvider instanceof ISelectionHandler) {
final ISelectionHandler selectionHandler = (ISelectionHandler) selectionProvider;
if (selectionHandler.canHandleSelection(selection)) {
Display.getDefault().syncExec(new Runnable(){
@Override
public void run() {
selectionHandler.setSelection(selection);
}
});
}
}
else {
Display.getDefault().syncExec(new Runnable(){
@Override
public void run() {
selectionProvider.setSelection(selection);
}
});
}
}
}
/**
* Creates the Control which is then returned in {@link #createResultViewerControl(Composite)}
* @param parent the parent composite
* @return the Control which is then returned in {@link #createResultViewerControl(Composite)}
*/
protected abstract Control doCreateResultViewerControl(Composite parent);
@Override
public Control createResultViewerControl(Composite parent) {
Control control = doCreateResultViewerControl(parent);
if (control instanceof ISelectionProvider) {
((ISelectionProvider)control).addSelectionChangedListener(
getSelectionChangedListener());
}
return control;
}
/**
* Subclasses can override this method if they need a special ISelectionChangedListener
* for their implementation
* @return the SelectionListener for your implementation
*/
protected ISelectionChangedListener getSelectionChangedListener()
{
return new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
if (!(event.getSelection() instanceof IStructuredSelection))
throw new ClassCastException("selection is an instance of "+(event.getSelection()==null?"null":event.getSelection().getClass().getName())+" instead of "+IStructuredSelection.class.getName()+"!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
IStructuredSelection sel = (IStructuredSelection) event.getSelection();
Object elem = sel.getFirstElement();
ProductTypeID productTypeID = (ProductTypeID) JDOHelper.getObjectId(elem);
setSelectedProductTypeID(productTypeID);
}
};
}
@Override
public boolean canHandleSelection(ISelection selection) {
if (getResultViewerControl() instanceof ISelectionHandler) {
ISelectionHandler selectionHandler = (ISelectionHandler) getResultViewerControl();
return selectionHandler.canHandleSelection(selection);
}
else
return false;
}
@Override
public void search(ProgressMonitor monitor, boolean inJob) {
if (! inJob) {
new Job(Messages.getString("org.nightlabs.jfire.trade.ui.producttype.quicklist.AbstractProductTypeQuickListFilter.job.search")) { //$NON-NLS-1$
@Override
protected IStatus run(ProgressMonitor monitor) {
search(monitor);
return Status.OK_STATUS;
}
}.schedule();
} else {
search(monitor);
}
}
// public void setVendorID(AnchorID vendorID) {
// getQuery().setVendorID(vendorID);
// }
/**
* Performs the search with the {@link QueryCollection} returned by {@link #getQueryCollection(ProgressMonitor)}.
*
* @param monitor the progressMonitor to show the progress
*/
protected abstract void search(ProgressMonitor monitor);
// public abstract Class<?> getQueryResultClass();
@Override
public Class<?> getQueryResultClass() {
return createQuery().getResultClass();
}
/**
* Returns the subclass of the {@link VendorDependentQuery} which is used
* for searching.
* @return the subclass of the {@link VendorDependentQuery} which is used
* for searching.
*/
protected abstract Class<? extends VendorDependentQuery> getQueryClass();
// protected abstract VendorDependentQuery createQuery();
/**
* Creates an instance of the subclass of VendorDependentQuery which is used for searching
* By default this is done by calling <code>getQueryClass().newInstance()</code>, if your implementation
* of VendorDependentQuery does not have an default constructor, override this method
* and return an instance of it.
* @return an instance of the VendorDependentQuery of the type return by {@link #getQueryClass()}
*/
protected VendorDependentQuery createQuery() {
try {
return getQueryClass().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Configures the Query return by {@link #createQuery()}.
* @param query the query to configure
*/
protected void configureQuery(VendorDependentQuery query) {
if (query instanceof AbstractProductTypeQuery) {
AbstractProductTypeQuery productTypeQuery = (AbstractProductTypeQuery) query;
productTypeQuery.setSaleable(true);
productTypeQuery.setPermissionGrantedToSell(true);
}
if (query instanceof AbstractProductTypeGroupQuery) {
AbstractProductTypeGroupQuery productTypeGroupQuery = (AbstractProductTypeGroupQuery) query;
productTypeGroupQuery.setSaleable(true);
productTypeGroupQuery.setPermissionGrantedToSell(true);
}
}
@Override
public QueryCollection<VendorDependentQuery> getQueryCollection(ProgressMonitor monitor)
{
monitor.beginTask(Messages.getString("org.nightlabs.jfire.trade.ui.producttype.quicklist.AbstractProductTypeQuickListFilter.job.loadQuery"), 100); //$NON-NLS-1$
if (queryCollection == null)
{
QueryStore defQueryStore;
// TODO this should be done in the server in order to make it [nearly] impossible that 2 defaults are created!
synchronized (AbstractProductTypeQuickListFilter.class) { // prevent having 2 defaultQueryStores due to multiple threads
defQueryStore = QueryStoreDAO.sharedInstance().getDefaultQueryStore(
getQueryResultClass(), Login.sharedInstance().getUserObjectID(),
FETCH_GROUPS_QUERY_STORE, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT,
new SubProgressMonitor(monitor, 50));
if (defQueryStore == null) {
VendorDependentQuery query = createQuery();
configureQuery(query);
queryCollection = new QueryCollection<VendorDependentQuery>(getQueryResultClass());
queryCollection.add(query);
User owner = Login.sharedInstance().getUser(new String[] {FetchPlan.DEFAULT}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT,
new org.eclipse.core.runtime.NullProgressMonitor());
defQueryStore = new BaseQueryStore(owner, IDGenerator.nextID(BaseQueryStore.class), queryCollection);
defQueryStore.setDefaultQuery(true);
if (logger.isDebugEnabled()) {
logger.debug("No default query store available, create one for resultClass = "+getQueryResultClass()+" and user "+Login.sharedInstance().getUserObjectID()); //$NON-NLS-1$ //$NON-NLS-2$
}
defQueryStore = QueryStoreDAO.sharedInstance().storeQueryStore(defQueryStore,
FETCH_GROUPS_QUERY_STORE, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT,
true, new SubProgressMonitor(monitor, 50));
}
}
if (queryCollection == null && defQueryStore != null) {
defaultQueryStore = defQueryStore;
QueryCollection qc = defQueryStore.getQueryCollection();
if (qc != null) {
queryCollection = qc;
if (queryCollection.isEmpty()) {
VendorDependentQuery query = createQuery();
configureQuery(query);
queryCollection.add(query);
}
}
}
if (queryCollection == null) {
throw new IllegalStateException("QueryCollection is still null how can that happen!"); //$NON-NLS-1$
}
}
monitor.done();
return queryCollection;
}
/**
* Create a key object that identifies the current query (also with vendor set).
* This key can be used to search and store the search results in the cache.
*/
protected QuickListFilterQueryResultKey createQueryResultCacheKey(ProgressMonitor monitor) {
QueryCollection<VendorDependentQuery> qc = getQueryCollection(monitor);
monitor.beginTask(Messages.getString("org.nightlabs.jfire.trade.ui.producttype.quicklist.AbstractProductTypeQuickListFilter.job.name"), 10); //$NON-NLS-1$
if (defaultQueryStore == null && qc != null) {
// FIXME: If this is null we have to obtain it this way, as a queryCollection does not know its QueryStore,
// this should done more efficiently
UserID userID = Login.sharedInstance().getUserObjectID();
QueryStore qs = QueryStoreDAO.sharedInstance().getDefaultQueryStore(
queryCollection.getResultClass(), userID,
new String[] {FetchPlan.DEFAULT},
NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT,
new SubProgressMonitor(monitor, 5));
this.defaultQueryStore = qs;
}
Iterator<VendorDependentQuery> it = qc.iterator();
VendorDependentQuery query = it.hasNext() ? it.next() : null;
monitor.done();
queryResultKey = new QuickListFilterQueryResultKey(defaultQueryStore, query != null ? query.getVendorID() : null);
return queryResultKey;
}
@Override
public void clearCache() {
Cache.sharedInstance().removeByObjectID(queryResultKey, false);
}
@Override
public void setQueryCollection(QueryCollection<VendorDependentQuery> queryCollection) {
if (queryCollection == null)
throw new IllegalArgumentException("QueryCollection must not be null!"); //$NON-NLS-1$
if (!queryCollection.getResultClass().equals(getQueryResultClass())) {
throw new IllegalArgumentException("The resultClass of the given queryCollection is "+queryCollection.getResultClass()+" but it should be "+getQueryResultClass()); //$NON-NLS-1$ //$NON-NLS-2$
}
this.queryCollection = queryCollection;
QuickListFilterQueryResultKey cacheKey = createQueryResultCacheKey(new NullProgressMonitor());
Cache.sharedInstance().removeByObjectID(cacheKey, false);
this.defaultQueryStore = null;
}
}