package rocks.inspectit.ui.rcp.wizard.page;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.nebula.widgets.cdatetime.CDT;
import org.eclipse.nebula.widgets.cdatetime.CDateTime;
import org.eclipse.swt.SWT;
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.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import rocks.inspectit.shared.cs.storage.processor.AbstractDataProcessor;
import rocks.inspectit.shared.cs.storage.processor.impl.TimeFrameDataProcessor;
/**
* Wizard page where user can limit the time frame for the storage write/record. The time frame can
* be in past or future, depending on the {@link #timeStyle} value.
*
* @author Ivan Senic
*
*/
public class DefineTimelineWizardPage extends WizardPage {
/**
* Time style value for the selecting future time.
*/
public static final int FUTURE = 1;
/**
* Time style value for the selecting past time.
*/
public static final int PAST = 2;
/**
* Style that enables the selection of both dates.
*/
public static final int BOTH_DATES = 4;
/**
* Map of the time characters associated to the multiplier of a second.
*/
private static final Map<Character, Integer> PERIOD_MULTIPLIERS_MAP = new HashMap<>(4);
static {
PERIOD_MULTIPLIERS_MAP.put('m', 1);
PERIOD_MULTIPLIERS_MAP.put('h', 60);
PERIOD_MULTIPLIERS_MAP.put('d', 60 * 24);
PERIOD_MULTIPLIERS_MAP.put('w', 60 * 24 * 7);
}
/**
* Time style. The style is defining if the page will display the future selection or past time.
*
* @see DefineTimelineWizardPage#FUTURE
* @see DefineTimelineWizardPage#PAST
*/
private int timeStyle;
/** button for use time frame dividing. */
private Button useTimeframe;
/** button to the define the period. */
private Button definePeriod;
/** button to define the date. */
private Button defineDate;
/** the primary date. */
private CDateTime cdtPrimary;
/** the secondary date. */
private CDateTime cdtSecondary;
/** the period. */
private Text periodAmount;
/** the group. */
private Group group;
/** the main composite. */
private Composite main;
/** the composite holding the period. */
private Composite periodComposite;
/** the composite holding the dates. */
private Composite cdtComposite;
/**
* Listener for the page completeness.
*/
private Listener pageCompleteListener = new Listener() {
@Override
public void handleEvent(Event event) {
setPageComplete(isPageComplete());
}
};
/**
* Default message of view.
*/
private String defaultMessage;
/**
* Default constructor.
*
* @param pageName
* Page name.
* @param defaultMessage
* Default message displayed to the user on the page.
* @param timeStyle
* Time style. The style is defining if the page will display the future selection or
* past time.
* @see DefineTimelineWizardPage#FUTURE
* @see DefineTimelineWizardPage#PAST
*/
public DefineTimelineWizardPage(String pageName, String defaultMessage, int timeStyle) {
super(pageName);
setTitle(pageName);
setMessage(defaultMessage);
this.timeStyle = timeStyle;
this.defaultMessage = defaultMessage;
}
/**
* {@inheritDoc}
*/
@Override
public void createControl(Composite parent) {
main = new Composite(parent, SWT.NONE);
main.setLayout(new GridLayout(1, false));
useTimeframe = new Button(main, SWT.CHECK);
useTimeframe.setText("Use timeframe limiting");
useTimeframe.setSelection(false);
group = new Group(main, SWT.NONE);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
group.setLayout(new GridLayout(1, false));
definePeriod = new Button(group, SWT.RADIO);
definePeriod.setText("Enter wanted time period");
definePeriod.setSelection(true);
defineDate = new Button(group, SWT.RADIO);
defineDate.setText("Selected exact date");
if ((timeStyle & BOTH_DATES) != 0) {
defineDate.setText(defineDate.getText() + "s");
}
defineDate.setSelection(false);
useTimeframe.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (useTimeframe.getSelection()) {
enableControlAndChildren(group, true);
} else {
enableControlAndChildren(group, false);
}
}
});
SelectionAdapter changeWayListener = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updatePage();
}
};
defineDate.addSelectionListener(changeWayListener);
definePeriod.addSelectionListener(changeWayListener);
useTimeframe.addListener(SWT.Selection, pageCompleteListener);
defineDate.addListener(SWT.Selection, pageCompleteListener);
definePeriod.addListener(SWT.Selection, pageCompleteListener);
updatePage();
enableControlAndChildren(group, false);
setControl(main);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPageComplete() {
if (useTimeframe.getSelection()) {
if (definePeriod.getSelection()) {
Date date = getDateFromPeriodComposite();
if (null == date) {
setMessage("Time period has to be entered correctly", ERROR);
return false;
}
} else {
if (null == this.getFromDate()) {
setMessage("Please enter starting date & time", ERROR);
return false;
} else if (null == this.getToDate()) {
setMessage("Please enter ending date & time", ERROR);
return false;
} else if (this.getFromDate().after(this.getToDate())) {
if ((timeStyle & BOTH_DATES) != 0) {
setMessage("Start date must be before end date", ERROR);
} else {
setMessage("Entered date must be a future date", ERROR);
}
return false;
}
}
}
setMessage(defaultMessage);
return true;
}
/**
* @return If the time frame is used.
*/
public boolean isTimerframeUsed() {
return useTimeframe.getSelection();
}
/**
* Returns the properly initialized {@link TimeFrameDataProcessor}.
*
* @param chainedProcessors
* Processors that need to be chained to {@link TimeFrameDataProcessor}.
* @return {@link TimeFrameDataProcessor}
* @see {AbstractChainedDataProcessor}
*/
public TimeFrameDataProcessor getTimeFrameDataProcessor(Collection<AbstractDataProcessor> chainedProcessors) {
List<AbstractDataProcessor> normalProcessors = new ArrayList<>(chainedProcessors);
Date fromDate = getFromDate();
Date toDate = getToDate();
TimeFrameDataProcessor timeFrameDataProcessor = new TimeFrameDataProcessor(fromDate, toDate, normalProcessors);
return timeFrameDataProcessor;
}
/**
* @return Returns from date. Note that if time style is future, here the current date will be
* returned.
*/
public Date getFromDate() {
if ((timeStyle & FUTURE) != 0) {
if ((timeStyle & BOTH_DATES) != 0) {
return (null != cdtPrimary) ? cdtPrimary.getSelection() : null; // NOPMD
} else {
return new Date();
}
} else {
if (definePeriod.getSelection()) {
return getDateFromPeriodComposite();
} else {
return (null != cdtPrimary) ? cdtPrimary.getSelection() : null; // NOPMD
}
}
}
/**
* @return Returns from date. Note that if time style is past, here the current date will be
* returned.
*/
public Date getToDate() {
if ((timeStyle & FUTURE) != 0) {
if (definePeriod.getSelection()) {
return getDateFromPeriodComposite();
} else if ((timeStyle & BOTH_DATES) != 0) {
return (null != cdtSecondary) ? cdtSecondary.getSelection() : null; // NOPMD
} else {
return (null != cdtPrimary) ? cdtPrimary.getSelection() : null; // NOPMD
}
} else {
if ((timeStyle & BOTH_DATES) != 0) {
return (null != cdtSecondary) ? cdtSecondary.getSelection() : null; // NOPMD
} else {
return new Date();
}
}
}
/**
* Enables or disables composite and all its children.
*
* @param composite
* Composite to enable.
* @param enabled
* True for enabling, false for disabling.
*/
private void enableControlAndChildren(Composite composite, boolean enabled) {
composite.setEnabled(enabled);
for (Control child : composite.getChildren()) {
child.setEnabled(enabled);
if (child instanceof Composite) {
enableControlAndChildren((Composite) child, enabled);
}
}
}
/**
* Updates the widgets on the page based on the selection.
*/
private void updatePage() {
if ((null != cdtComposite) && !cdtComposite.isDisposed()) {
cdtComposite.dispose();
}
if ((null != periodComposite) && !periodComposite.isDisposed()) {
periodComposite.dispose();
}
if (definePeriod.getSelection()) {
periodComposite = new Composite(group, SWT.NONE);
periodComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
periodComposite.setLayout(new GridLayout(3, false));
Label text = new Label(periodComposite, SWT.NONE);
periodAmount = new Text(periodComposite, SWT.SINGLE | SWT.BORDER);
periodAmount.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
periodAmount.addListener(SWT.Modify, pageCompleteListener);
periodAmount.setFocus();
new Label(periodComposite, SWT.NONE).setText("(ex. 2w 4d 12h 30m)");
if ((timeStyle & FUTURE) != 0) {
text.setText("Next:");
} else {
text.setText("Previous:");
}
} else {
cdtComposite = new Composite(group, SWT.NONE);
cdtComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
cdtComposite.setLayout(new GridLayout(1, true));
cdtPrimary = new CDateTime(cdtComposite, CDT.BORDER | CDT.DROP_DOWN | CDT.TAB_FIELDS);
cdtPrimary.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
cdtPrimary.addListener(SWT.Modify, pageCompleteListener);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
if (((timeStyle & FUTURE) != 0) && ((timeStyle & BOTH_DATES) == 0)) {
cdtPrimary.setPattern("'Till\t\t' EEEE, MMMM d YYYY '@' h:mm a");
calendar.add(Calendar.HOUR, 1);
} else {
cdtPrimary.setPattern("'From\t' EEEE, MMMM d YYYY '@' h:mm a");
}
cdtPrimary.setSelection(calendar.getTime());
if ((timeStyle & BOTH_DATES) != 0) {
cdtSecondary = new CDateTime(cdtComposite, CDT.BORDER | CDT.DROP_DOWN | CDT.TAB_FIELDS);
cdtSecondary.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
calendar.add(Calendar.HOUR, 1);
cdtSecondary.setSelection(calendar.getTime());
cdtSecondary.addListener(SWT.Modify, pageCompleteListener);
cdtSecondary.setPattern("'Till\t\t' EEEE, MMMM d YYYY '@' h:mm a");
}
}
group.layout();
main.layout();
}
/**
* @return Gets date from the date composite.
*/
private Date getDateFromPeriodComposite() {
try {
Calendar cal = Calendar.getInstance();
String periodString = periodAmount.getText().trim();
StringTokenizer tokenizer = new StringTokenizer(periodString, " ");
int perAmountInMinutes = 0;
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
Character timeChar = token.charAt(token.length() - 1);
Integer timeMultiplayer = PERIOD_MULTIPLIERS_MAP.get(timeChar);
if (null != timeMultiplayer) {
try {
int value = Integer.parseInt(token.substring(0, token.length() - 1));
if (value > 0) {
perAmountInMinutes += value * timeMultiplayer.intValue();
}
} catch (Exception e) {
continue;
}
}
}
if (perAmountInMinutes > 0) {
if ((timeStyle & FUTURE) != 0) {
cal.add(Calendar.MINUTE, perAmountInMinutes);
} else {
cal.add(Calendar.MINUTE, -(perAmountInMinutes));
}
return cal.getTime();
} else {
return null;
}
} catch (Exception e) {
return null;
}
}
}