package org.nightlabs.jfire.jbpm.ui.query;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jdo.FetchPlan;
import javax.jdo.JDOHelper;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.nightlabs.base.ui.composite.DateTimeEdit;
import org.nightlabs.base.ui.composite.XComboComposite;
import org.nightlabs.base.ui.composite.XComposite;
import org.nightlabs.base.ui.composite.XComposite.LayoutDataMode;
import org.nightlabs.base.ui.job.Job;
import org.nightlabs.jdo.NLJDOHelper;
import org.nightlabs.jdo.query.QueryEvent;
import org.nightlabs.jdo.query.QueryProvider;
import org.nightlabs.jdo.query.AbstractSearchQuery.FieldChangeCarrier;
import org.nightlabs.jfire.base.JFireEjb3Factory;
import org.nightlabs.jfire.base.login.ui.Login;
import org.nightlabs.jfire.base.ui.search.AbstractQueryFilterComposite;
import org.nightlabs.jfire.jbpm.JbpmManagerRemote;
import org.nightlabs.jfire.jbpm.dao.ProcessDefinitionDAO;
import org.nightlabs.jfire.jbpm.dao.StateDefinitionDAO;
import org.nightlabs.jfire.jbpm.graph.def.ProcessDefinition;
import org.nightlabs.jfire.jbpm.graph.def.Statable;
import org.nightlabs.jfire.jbpm.graph.def.StateDefinition;
import org.nightlabs.jfire.jbpm.graph.def.id.ProcessDefinitionID;
import org.nightlabs.jfire.jbpm.graph.def.id.StateDefinitionID;
import org.nightlabs.jfire.jbpm.query.StatableQuery;
import org.nightlabs.jfire.jbpm.ui.resource.Messages;
import org.nightlabs.l10n.IDateFormatter;
import org.nightlabs.progress.NullProgressMonitor;
import org.nightlabs.progress.ProgressMonitor;
import org.nightlabs.util.Util;
/**
* @author Daniel Mazurek
* @author Marius Heinzmann - marius[at]nightlabs[dot]com
*/
public abstract class AbstractStatableSearchComposite<Q extends StatableQuery>
extends AbstractQueryFilterComposite<Q>
{
private DateTimeEdit createDTMin;
private DateTimeEdit createDTMax;
private Button stateDefinitionActiveButton;
private Button onlyInSelectedStateButton;
private Button notInSelectedStateButton;
private final Map<ProcessDefinition, List<StateDefinition>> processDefinition2StateDefinitions;
private static final String[] FETCH_GROUPS_STATE_DEFINITON = new String[] {
FetchPlan.DEFAULT,
StateDefinition.FETCH_GROUP_NAME
};
private static final String[] FETCH_GROUPS_PROCESS_DEFINITON = new String[] {
FetchPlan.DEFAULT
};
/**
* Creates a new {@link AbstractQueryFilterComposite}.
* <p><b>Note</b>: The caller has to call {@link #createComposite()} to create the UI! <br />
* This is not done in this constructor to omit problems with fields that are not only declared,
* but also initialised. If these fields are used inside {@link #createComposite()}
* or new values are assigned to them, one of the following two things may happen:
* <ul>
* <li>The value assigned to that field is overridden by the initialisation value that is
* assigned after this constructor is finished</li>
* <li>The referenced value is not yet properly initialised, because the initialisation is
* done after the constructor finishes, and hence results in an unexpected exception.</li>
* </ul>
* </p>
* @param parent
* The parent to instantiate this filter into.
* @param style
* The style to apply.
* @param layoutMode
* The layout mode to use. See {@link XComposite.LayoutMode}.
* @param layoutDataMode
* The layout data mode to use. See {@link XComposite.LayoutDataMode}.
* @param queryProvider
* The queryProvider to use. It may be <code>null</code>, but the caller has to
* ensure, that it is set before {@link #getQuery()} is called!
*/
public AbstractStatableSearchComposite(final Composite parent, final int style, final LayoutMode layoutMode,
final LayoutDataMode layoutDataMode, final QueryProvider<? super StatableQuery> queryProvider)
{
super(parent, style, layoutMode, layoutDataMode, queryProvider);
processDefinition2StateDefinitions = new HashMap<ProcessDefinition, List<StateDefinition>>();
createComposite();
}
/**
* Delegates to {@link StatableFilterComposite#StatableFilterComposite(AbstractQueryFilterComposite, int, XComposite.LayoutMode, LayoutDataMode)}
*/
public AbstractStatableSearchComposite(final Composite parent, final int style,
final QueryProvider<? super StatableQuery> queryProvider)
{
super(parent, style, queryProvider);
processDefinition2StateDefinitions = new HashMap<ProcessDefinition, List<StateDefinition>>();
createComposite();
}
// @Override
// public Class<Q> getQueryClass()
// {
// return StatableQuery.class;
// }
@Override
protected void createComposite()
{
final Group group = new Group(this, SWT.NONE);
group.setText(Messages.getString("org.nightlabs.jfire.jbpm.ui.query.AbstractStatableSearchComposite.group.state.text")); //$NON-NLS-1$
group.setLayout(new GridLayout(4, false));
group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
final XComposite wrapper = new XComposite(group, SWT.NONE, LayoutMode.TIGHT_WRAPPER,
LayoutDataMode.GRID_DATA_HORIZONTAL, 3);
wrapper.getGridLayout().makeColumnsEqualWidth = true;
wrapper.getGridData().horizontalSpan = 2;
stateDefinitionActiveButton = new Button(wrapper, SWT.CHECK);
stateDefinitionActiveButton.setText(Messages.getString("org.nightlabs.jfire.jbpm.ui.query.AbstractStatableSearchComposite.button.active.text")); //$NON-NLS-1$
stateDefinitionActiveButton.addSelectionListener(new ButtonSelectionListener()
{
@Override
protected void handleSelection(final boolean active)
{
getQuery().setFieldEnabled(StatableQuery.FieldName.stateDefinitionID, active);
}
});
onlyInSelectedStateButton = new Button(wrapper, SWT.CHECK);
onlyInSelectedStateButton.setText(Messages.getString("org.nightlabs.jfire.jbpm.ui.query.AbstractStatableSearchComposite.button.onlyInSelectedState.text")); //$NON-NLS-1$
final GridData selectedStateButtonData = new GridData();
// selectedStateButtonData.horizontalSpan = 2;
onlyInSelectedStateButton.setLayoutData(selectedStateButtonData);
onlyInSelectedStateButton.setEnabled(false);
onlyInSelectedStateButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(final SelectionEvent e)
{
final Boolean onlyInSelectedState = onlyInSelectedStateButton.getSelection();
getQuery().setOnlyInSelectedState(onlyInSelectedState);
final StateDefinitionID stateDefinitionID = (StateDefinitionID)
JDOHelper.getObjectId(stateDefinitionsCombo.getSelectedElement());
getQuery().setStateDefinitionID(stateDefinitionID);
}
});
notInSelectedStateButton = new Button(wrapper, SWT.CHECK);
notInSelectedStateButton.setText(Messages.getString("org.nightlabs.jfire.jbpm.ui.query.AbstractStatableSearchComposite.button.notInSelectedState.text")); //$NON-NLS-1$
final GridData notSelectedStateButtonData = new GridData();
// selectedStateButtonData.horizontalSpan = 2;
notInSelectedStateButton.setLayoutData(notSelectedStateButtonData);
notInSelectedStateButton.setEnabled(false);
notInSelectedStateButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(final SelectionEvent e)
{
final Boolean notInSelectedState = notInSelectedStateButton.getSelection();
getQuery().setNotInSelectedState(notInSelectedState);
final StateDefinitionID stateDefinitionID = (StateDefinitionID)
JDOHelper.getObjectId(stateDefinitionsCombo.getSelectedElement());
getQuery().setStateDefinitionID(stateDefinitionID);
}
});
processDefinitionsCombo = new XComboComposite<ProcessDefinition>(wrapper, SWT.READ_ONLY | getBorderStyle(), processDefinitionLabelProvider);
final GridData data2 = new GridData(GridData.FILL_HORIZONTAL);
data2.horizontalSpan = 1;
processDefinitionsCombo.setLayoutData(data2);
processDefinitionsCombo.setEnabled(false);
processDefinitionsCombo.addSelectionChangedListener(new ISelectionChangedListener()
{
@Override
public void selectionChanged(final SelectionChangedEvent e)
{
final ProcessDefinition selectedProcessDefinition = processDefinitionsCombo.getSelectedElement();
final List<StateDefinition> stateDefinitions = processDefinition2StateDefinitions.get(selectedProcessDefinition);
stateDefinitionsCombo.removeAll();
stateDefinitionsCombo.addElements(stateDefinitions);
if (!stateDefinitionsCombo.getElements().isEmpty())
stateDefinitionsCombo.selectElementByIndex(0);
}
});
stateDefinitionsCombo = new XComboComposite<StateDefinition>(wrapper, SWT.READ_ONLY | getBorderStyle(), stateDefinitionLabelProvider);
final GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 2;
stateDefinitionsCombo.setLayoutData(data);
stateDefinitionsCombo.setEnabled(false);
stateDefinitionsCombo.addSelectionChangedListener(new ISelectionChangedListener()
{
@Override
public void selectionChanged(final SelectionChangedEvent e)
{
final StateDefinitionID stateDefinitionID = (StateDefinitionID)
JDOHelper.getObjectId(stateDefinitionsCombo.getSelectedElement());
getQuery().setStateDefinitionID(stateDefinitionID);
}
});
createDTMin = new DateTimeEdit(
group,
IDateFormatter.FLAGS_DATE_SHORT_TIME_HMS_WEEKDAY + DateTimeEdit.FLAGS_SHOW_ACTIVE_CHECK_BOX,
Messages.getString("org.nightlabs.jfire.jbpm.ui.query.AbstractStatableSearchComposite.button.minCreateDT.text")); //$NON-NLS-1$
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND));
createDTMin.setDate(cal.getTime());
createDTMin.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
createDTMin.addModifyListener(new ModifyListener()
{
@Override
public void modifyText(final ModifyEvent me)
{
final Date createDTMinDate = createDTMin.getDate();
getQuery().setStateCreateDTMin(createDTMinDate);
}
});
createDTMin.addActiveChangeListener(new ButtonSelectionListener()
{
@Override
protected void handleSelection(final boolean active)
{
getQuery().setFieldEnabled(StatableQuery.FieldName.stateCreateDTMin, active);
}
});
createDTMax = new DateTimeEdit(
group,
IDateFormatter.FLAGS_DATE_SHORT_TIME_HMS_WEEKDAY + DateTimeEdit.FLAGS_SHOW_ACTIVE_CHECK_BOX,
Messages.getString("org.nightlabs.jfire.jbpm.ui.query.AbstractStatableSearchComposite.button.maxCreateDT.text")); //$NON-NLS-1$
cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY));
cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE));
cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND));
cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND));
createDTMax.setDate(cal.getTime());
createDTMax.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
createDTMax.addModifyListener(new ModifyListener()
{
@Override
public void modifyText(final ModifyEvent me)
{
final Date createDTMaxDate = createDTMax.getDate();
getQuery().setStateCreateDTMax(createDTMaxDate);
}
});
createDTMax.addActiveChangeListener(new ButtonSelectionListener()
{
@Override
protected void handleSelection(final boolean active)
{
getQuery().setFieldEnabled(StatableQuery.FieldName.stateCreateDTMax, active);
}
});
}
private XComboComposite<StateDefinition> stateDefinitionsCombo;
private final ILabelProvider stateDefinitionLabelProvider = new LabelProvider() {
@Override
public String getText(final Object element)
{
if (element instanceof StateDefinition) {
final StateDefinition stateDefinition = (StateDefinition) element;
return stateDefinition.getName().getText();
}
return super.getText(element);
}
};
private XComboComposite<ProcessDefinition> processDefinitionsCombo;
private final ILabelProvider processDefinitionLabelProvider = new LabelProvider() {
@Override
public String getText(final Object element)
{
if (element instanceof ProcessDefinition) {
final ProcessDefinition processDefinition = (ProcessDefinition) element;
return processDefinition.getProcessDefinitionID();
}
return super.getText(element);
}
};
private Class<? extends Statable> statableClass;
private Job fillStateComboJob;
/**
* Sets the class implementing {@link Statable} for which the states shall be retrieved and used
* for filtering.
*
* @param statableClass a class implementing Statable
*/
public void setStatableClass(final Class<? extends Statable> statableClass)
{
assert statableClass != null;
if (statableClass == this.statableClass)
return;
this.statableClass = statableClass;
getQuery().setStatableClass(statableClass);
final String statableClassName = statableClass.getName();
fillStateComboJob = new Job(Messages.getString("org.nightlabs.jfire.jbpm.ui.query.AbstractStatableSearchComposite.job.loadProcessDefinition.text")) { //$NON-NLS-1$
@Override
protected IStatus run(final ProgressMonitor monitor) {
try {
// TradeManagerRemote tradeManager = JFireEjb3Factory.getRemoteBean(TradeManagerRemote.class, Login.getLogin().getInitialContextProperties());
final JbpmManagerRemote jbpmManager = JFireEjb3Factory.getRemoteBean(JbpmManagerRemote.class, Login.getLogin().getInitialContextProperties());
// TODO: add workflow selection so that only the processdefiniton for the given workflow
// Set<ProcessDefinitionID> processDefinitionIDs = tradeManager.getProcessDefinitionIDs(statableClassName);
final Set<ProcessDefinitionID> processDefinitionIDs = retrieveProcessDefinitionIDs(monitor, statableClass);
final String[] PROCESS_DEFINITION_FETCH_GROUPS = new String[] {
FetchPlan.DEFAULT,
ProcessDefinition.FETCH_GROUP_THIS_PROCESS_DEFINITION
};
final Collection<ProcessDefinition> processDefinitions = ProcessDefinitionDAO.sharedInstance().getProcessDefinitions(
processDefinitionIDs,
PROCESS_DEFINITION_FETCH_GROUPS,
NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT,
monitor);
for (final ProcessDefinition processDefinition : processDefinitions)
{
final Set<StateDefinitionID> statedDefinitionIDs = jbpmManager.getStateDefinitionIDs(processDefinition);
final Collection<StateDefinition> stateDefinitions = StateDefinitionDAO.sharedInstance().getStateDefintions(
statedDefinitionIDs,
FETCH_GROUPS_STATE_DEFINITON,
NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT,
monitor);
// TODO: sort stateDefinitions
processDefinition2StateDefinitions.put(processDefinition, new ArrayList<StateDefinition>(stateDefinitions));
}
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (stateDefinitionsCombo == null || stateDefinitionsCombo.isDisposed())
return;
processDefinitionsCombo.addElements(processDefinitions);
if (!processDefinitions.isEmpty()) {
final ProcessDefinition firstProcessDefinition = processDefinitions.iterator().next();
processDefinitionsCombo.selectElement(firstProcessDefinition);
final List<StateDefinition> stateDefinitionList = processDefinition2StateDefinitions.get(firstProcessDefinition);
stateDefinitionsCombo.addElements(stateDefinitionList);
if (!stateDefinitionList.isEmpty()) {
stateDefinitionsCombo.selectElementByIndex(0);
}
}
if (deferredSelectedStateDefinition != null) {
stateDefinitionsCombo.selectElement(deferredSelectedStateDefinition);
deferredSelectedStateDefinition = null;
}
}
});
} catch (final Exception e) {
throw new RuntimeException(e);
}
return Status.OK_STATUS;
}
};
fillStateComboJob.schedule();
}
private static final String STATABLE_GROUP_ID = "StatableFilterGroup"; //$NON-NLS-1$
private static final Set<String> fieldNames;
static
{
fieldNames = new HashSet<String>();
fieldNames.add(StatableQuery.FieldName.onlyInSelectedState);
fieldNames.add(StatableQuery.FieldName.notInSelectedState);
fieldNames.add(StatableQuery.FieldName.stateCreateDTMax);
fieldNames.add(StatableQuery.FieldName.stateCreateDTMin);
fieldNames.add(StatableQuery.FieldName.stateDefinitionID);
}
@Override
protected Set<String> getFieldNames()
{
return fieldNames;
}
@Override
protected String getGroupID()
{
return STATABLE_GROUP_ID;
}
private StateDefinition deferredSelectedStateDefinition = null;
private ProcessDefinition deferredProcessDefinition = null;
@Override
protected void updateUI(final QueryEvent event, final List<FieldChangeCarrier> changedFields)
{
if (Display.getCurrent() == null)
throw new IllegalStateException("This method must be invoked on the SWT UI thread!"); //$NON-NLS-1$
for (final FieldChangeCarrier fieldChange : changedFields)
{
if (StatableQuery.FieldName.onlyInSelectedState.equals(fieldChange.getPropertyName()))
{
onlyInSelectedStateButton.setSelection((Boolean) fieldChange.getNewValue());
}
else if (StatableQuery.FieldName.notInSelectedState.equals(fieldChange.getPropertyName()))
{
notInSelectedStateButton.setSelection((Boolean) fieldChange.getNewValue());
}
else if (StatableQuery.FieldName.stateCreateDTMax.equals(fieldChange.getPropertyName()))
{
final Date maxDate = (Date) fieldChange.getNewValue();
createDTMax.setDate(maxDate);
}
else if (getEnableFieldName(StatableQuery.FieldName.stateCreateDTMax).equals(
fieldChange.getPropertyName()))
{
final Boolean active = (Boolean) fieldChange.getNewValue();
createDTMax.setActive(active);
setSearchSectionActive(active);
}
else if (StatableQuery.FieldName.stateCreateDTMin.equals(fieldChange.getPropertyName()))
{
final Date minDate = (Date) fieldChange.getNewValue();
createDTMin.setDate(minDate);
}
else if (getEnableFieldName(StatableQuery.FieldName.stateCreateDTMin).equals(
fieldChange.getPropertyName()))
{
final Boolean active = (Boolean) fieldChange.getNewValue();
createDTMin.setActive(active);
setSearchSectionActive(active);
}
else if (StatableQuery.FieldName.stateDefinitionID.equals(fieldChange.getPropertyName()))
{
final StateDefinitionID tmpID = (StateDefinitionID) fieldChange.getNewValue();
if (tmpID == null)
{
stateDefinitionsCombo.setSelection((StateDefinition) null);
}
else
{
StateDefinition selection;
try
{
selection = StateDefinitionDAO.sharedInstance().getStateDefintions(
Collections.singleton(tmpID), FETCH_GROUPS_STATE_DEFINITON,
NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new NullProgressMonitor()
).iterator().next();
}
catch (final Exception e)
{
if (e instanceof RuntimeException)
{
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
stateDefinitionsCombo.setSelection(selection);
if (!Util.equals(selection, stateDefinitionsCombo.getSelectedElement()))
deferredSelectedStateDefinition = selection;
}
}
else if (getEnableFieldName(StatableQuery.FieldName.stateDefinitionID).equals(
fieldChange.getPropertyName()))
{
final Boolean active = (Boolean) fieldChange.getNewValue();
stateDefinitionsCombo.setEnabled(active);
onlyInSelectedStateButton.setEnabled(active);
notInSelectedStateButton.setEnabled(active);
processDefinitionsCombo.setEnabled(active);
setSearchSectionActive(stateDefinitionActiveButton, active);
}
else if (StatableQuery.FieldName.processDefinitionID.equals(fieldChange.getPropertyName()))
{
final ProcessDefinitionID tmpID = (ProcessDefinitionID) fieldChange.getNewValue();
if (tmpID == null)
{
processDefinitionsCombo.setSelection((ProcessDefinition) null);
}
else
{
ProcessDefinition selection;
try
{
selection = ProcessDefinitionDAO.sharedInstance().getProcessDefinitions(
Collections.singleton(tmpID), FETCH_GROUPS_PROCESS_DEFINITON,
NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new NullProgressMonitor()
).iterator().next();
}
catch (final Exception e)
{
if (e instanceof RuntimeException)
{
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
processDefinitionsCombo.setSelection(selection);
if (!Util.equals(selection, processDefinitionsCombo.getSelectedElement()))
deferredProcessDefinition = selection;
}
}
else if (getEnableFieldName(StatableQuery.FieldName.processDefinitionID).equals(
fieldChange.getPropertyName()))
{
final Boolean active = (Boolean) fieldChange.getNewValue();
stateDefinitionsCombo.setEnabled(active);
onlyInSelectedStateButton.setEnabled(active);
notInSelectedStateButton.setEnabled(active);
processDefinitionsCombo.setEnabled(active);
setSearchSectionActive(stateDefinitionActiveButton, active);
}
} // for (FieldChangeCarrier fieldChange : event.getChangedFields().values())
}
protected abstract Set<ProcessDefinitionID> retrieveProcessDefinitionIDs(ProgressMonitor monitor, Class<? extends Statable> statableClass);
}