/*
* ARX: Powerful Data Anonymization
* Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer and contributors
*
* 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 org.deidentifier.arx.gui.view.impl.menu;
import java.util.ArrayList;
import java.util.List;
import org.deidentifier.arx.Data;
import org.deidentifier.arx.DataSelector;
import org.deidentifier.arx.gui.resources.Resources;
import org.deidentifier.arx.gui.view.SWTUtil;
import org.deidentifier.arx.gui.view.def.IDialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.window.Window;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
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.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import de.linearbits.objectselector.ICallback;
import de.linearbits.objectselector.SelectorTokenizer;
/**
* Query dialog
*
* @author Fabian Prasser
*/
public class DialogQuery extends TitleAreaDialog implements IDialog {
/** TODO */
private Runnable updater = new Runnable(){
private DataSelector previous = null;
@Override
public void run() {
while (!stop){
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
// Ignore
}
DataSelector selector = null;
synchronized(DialogQuery.this){
selector = DialogQuery.this.selector;
}
if (selector != null && selector != previous){
previous = selector;
int count = 0;
for (int i=0; i<data.getHandle().getNumRows(); i++){
count += selector.isSelected(i) ? 1 : 0;
}
final int fcount = count;
if (status!=null && !status.isDisposed()){
Display.getDefault().asyncExec(new Runnable() {
public void run() {
status.setText(Resources.getMessage("QueryDialog.8")+fcount); //$NON-NLS-1$
}
});
}
}
}
}
};
/** TODO */
private static final int INTERVAL = 500;
/** TODO */
private Button ok = null;
/** TODO */
private Button cancel = null;
/** TODO */
private StyledText text = null;
/** TODO */
private Label status = null;
/** TODO */
private Data data = null;
/** TODO */
private String queryString = null;
/** TODO */
private DataSelector selector = null;
/** TODO */
private ICallback highlighter = null;
/** TODO */
private List<StyleRange> styles = new ArrayList<StyleRange>();
/** TODO */
private boolean stop = false;
/** TODO */
private List<Button> singleSelectionButtons = new ArrayList<Button>();
/** TODO */
private List<Button> multiSelectionButtons = new ArrayList<Button>();
/**
*
*
* @param data
* @param parent
* @param initial
*/
public DialogQuery(final Data data, final Shell parent, String initial) {
super(parent);
this.queryString = initial;
this.data = data;
}
@Override
public boolean close() {
return super.close();
}
/**
*
*
* @return
*/
public DialogQueryResult getResult() {
return new DialogQueryResult(queryString, selector);
}
/**
* Creates a new button with which elements can be added to the text
* @param text
* @param items
* @param group
* @param label
* @param openingSymbol
* @param closingSymbol
* @param span
* @param multi
*/
private void createButton(final StyledText text,
final Combo items,
final Group group,
final String label,
final String tooltip,
final String openingSymbol,
final String closingSymbol,
final int span,
final boolean multi) {
final Button button = new Button(group, SWT.PUSH);
button.setText(label);
button.setToolTipText(tooltip);
if (items != null) {
singleSelectionButtons.add(button);
button.setEnabled(true);
} else if (multi) {
multiSelectionButtons.add(button);
button.setEnabled(false);
} else {
singleSelectionButtons.add(button);
button.setEnabled(true);
}
button.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).span(span, 1).create());
button.addSelectionListener(new SelectionAdapter(){
public void widgetSelected(SelectionEvent arg0) {
// Prepare
Point selection = text.getSelectionRange();
StringBuilder builder = new StringBuilder();
String insert = openingSymbol;
// Handle special case with combo
if (items != null) {
if (selection.y != 0 || items.getSelectionIndex() == -1 ||
items.getItem(items.getSelectionIndex()) == null) {
return;
} else {
insert = "'" + items.getItem(items.getSelectionIndex()) + "'"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
// Create new content
if (multi && selection.y != 0) {
builder.append(text.getTextRange(0, selection.x));
builder.append(" "); //$NON-NLS-1$
builder.append(openingSymbol);
builder.append(" "); //$NON-NLS-1$
builder.append(text.getTextRange(selection.x, selection.y));
builder.append(" "); //$NON-NLS-1$
builder.append(closingSymbol);
builder.append(" "); //$NON-NLS-1$
builder.append(text.getTextRange(selection.x + selection.y, text.getText().length() - (selection.x + selection.y)));
} else if (!multi && selection.y == 0){
if (selection.x == 0){
builder.append(insert);
builder.append(" "); //$NON-NLS-1$
builder.append(text.getText());
} else if (selection.x == text.getText().length()){
builder.append(text.getText());
builder.append(" "); //$NON-NLS-1$
builder.append(insert);
} else {
builder.append(text.getText(0, selection.x - 1));
builder.append(" "); //$NON-NLS-1$
builder.append(insert);
builder.append(" "); //$NON-NLS-1$
builder.append(text.getText(selection.x, text.getText().length()-1));
}
} else {
return;
}
// Replace and highlight
text.setText(builder.toString());
highlight();
parse();
text.setSelection(text.getText().length());
updateButtons();
}
});
}
/**
*
*/
private void highlight() {
if (highlighter==null){
highlighter = new ICallback(){
@Override
public void and(int start, int length) {
StyleRange style = new StyleRange();
style.start = start;
style.length = length;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_GRAY;
styles.add(style);
}
@Override
public void begin(int start) {
StyleRange style = new StyleRange();
style.start = start;
style.length = 1;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_GREEN;
styles.add(style);
}
@Override
public void check() {
// ignore
}
@Override
public void end(int start) {
StyleRange style = new StyleRange();
style.start = start;
style.length = 1;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_GREEN;
styles.add(style);
}
@Override
public void equals(int start) {
StyleRange style = new StyleRange();
style.start = start;
style.length = 1;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_BLUE;
styles.add(style);
}
@Override
public void field(int start, int length) {
StyleRange style = new StyleRange();
style.start = start;
style.length = length;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_RED;
styles.add(style);
}
@Override
public void geq(int start, int length) {
StyleRange style = new StyleRange();
style.start = start;
style.length = length;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_BLUE;
styles.add(style);
}
@Override
public void greater(int start) {
StyleRange style = new StyleRange();
style.start = start;
style.length = 1;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_BLUE;
styles.add(style);
}
@Override
public void invalid(int start) {
// ignore
}
@Override
public void leq(int start, int length) {
StyleRange style = new StyleRange();
style.start = start;
style.length = length;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_BLUE;
styles.add(style);
}
@Override
public void less(int start) {
StyleRange style = new StyleRange();
style.start = start;
style.length = 1;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_BLUE;
styles.add(style);
}
@Override
public void neq(int start, int length) {
StyleRange style = new StyleRange();
style.start = start;
style.length = length;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_BLUE;
styles.add(style);
}
@Override
public void or(int start, int length) {
StyleRange style = new StyleRange();
style.start = start;
style.length = length;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_GRAY;
styles.add(style);
}
@Override
public void value(int start, int length) {
StyleRange style = new StyleRange();
style.start = start;
style.length = length;
style.fontStyle = SWT.BOLD;
style.foreground = GUIHelper.COLOR_DARK_GRAY;
styles.add(style);
}
};
}
styles.clear();
SelectorTokenizer<Integer> tokenizer = new SelectorTokenizer<Integer>(highlighter);
tokenizer.tokenize(text.getText());
text.setRedraw(false);
text.setStyleRanges(styles.toArray(new StyleRange[styles.size()]));
text.setRedraw(true);
}
/**
*
*/
private void parse() {
synchronized (this) {
// Query
final String query = text.getText();
final DataSelector selector;
try {
selector = DataSelector.create(data, query);
selector.build();
} catch (Exception e){
this.status.setText(e.getMessage());
this.ok.setEnabled(false);
this.selector = null;
return;
}
this.status.setText(Resources.getMessage("DialogQuery.10")); //$NON-NLS-1$
this.queryString = text.getText();
this.selector = selector;
this.ok.setEnabled(true);
}
}
/**
* Updates all buttons
*/
private void updateButtons() {
int selectionLength = text.getSelectionRange().y;
for (Button b : singleSelectionButtons) {
b.setEnabled(selectionLength == 0);
}
for (Button b : multiSelectionButtons) {
b.setEnabled(selectionLength != 0);
}
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setImages(Resources.getIconSet(newShell.getDisplay()));
}
@Override
protected void createButtonsForButtonBar(final Composite parent) {
parent.setLayoutData(SWTUtil.createFillGridData());
// Create OK Button
ok = createButton(parent, Window.OK, Resources.getMessage("ProjectDialog.3"), true); //$NON-NLS-1$
ok.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
setReturnCode(Window.OK);
stop = true;
close();
}
});
ok.setEnabled(false);
// Create Cancel Button
cancel = createButton(parent, Window.CANCEL, Resources.getMessage("ProjectDialog.4"), false); //$NON-NLS-1$
cancel.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
setReturnCode(Window.CANCEL);
stop = true;
close();
}
});
parse();
}
@Override
protected Control createContents(Composite parent) {
Control contents = super.createContents(parent);
setTitle(Resources.getMessage("QueryDialog.0")); //$NON-NLS-1$
setMessage(Resources.getMessage("QueryDialog.1"), IMessageProvider.NONE); //$NON-NLS-1$
return contents;
}
@Override
protected Control createDialogArea(final Composite parent) {
parent.setLayout(GridLayoutFactory.swtDefaults().numColumns(2).create());
Group query = new Group(parent, SWT.SHADOW_ETCHED_IN);
query.setText(Resources.getMessage("DialogQuery.11")); //$NON-NLS-1$
query.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
query.setLayout(GridLayoutFactory.swtDefaults().numColumns(1).spacing(5, 5).create());
text = new StyledText(query, SWT.BORDER | SWT.MULTI | SWT.WRAP);
text.setText(this.queryString);
text.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
text.addModifyListener(new ModifyListener(){
@Override
public void modifyText(ModifyEvent arg0) {
highlight();
parse();
}
});
// Update buttons
text.addSelectionListener(new SelectionAdapter(){
public void widgetSelected(SelectionEvent arg0) {
updateButtons();
}
});
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayoutData(GridDataFactory.fillDefaults().grab(false, true).create());
composite.setLayout(GridLayoutFactory.swtDefaults().numColumns(1).spacing(0, 0).margins(0, 0).create());
Group booleanOperators = new Group(composite, SWT.SHADOW_ETCHED_IN);
booleanOperators.setText(Resources.getMessage("DialogQuery.12")); //$NON-NLS-1$
booleanOperators.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
booleanOperators.setLayout(GridLayoutFactory.swtDefaults().numColumns(2).create());
createButton(text, null, booleanOperators, Resources.getMessage("DialogQuery.13"), Resources.getMessage("DialogQuery.14"), Resources.getMessage("DialogQuery.15"), "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, booleanOperators, Resources.getMessage("DialogQuery.17"), Resources.getMessage("DialogQuery.18"), Resources.getMessage("DialogQuery.19"), "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, booleanOperators, "(", Resources.getMessage("DialogQuery.22"), "(", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, booleanOperators, ")", Resources.getMessage("DialogQuery.26"), ")", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, booleanOperators, "( ... )", Resources.getMessage("DialogQuery.30"), "(", ")", 2, true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
Group relationalOperators = new Group(composite, SWT.SHADOW_ETCHED_IN);
relationalOperators.setText(Resources.getMessage("DialogQuery.33")); //$NON-NLS-1$
relationalOperators.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
relationalOperators.setLayout(GridLayoutFactory.swtDefaults().numColumns(2).create());
createButton(text, null, relationalOperators, "=", Resources.getMessage("DialogQuery.35"), "=", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, relationalOperators, "<>", Resources.getMessage("DialogQuery.39"), "<>", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, relationalOperators, "<", Resources.getMessage("DialogQuery.43"), "<", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, relationalOperators, "<=", Resources.getMessage("DialogQuery.47"), "<=", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, relationalOperators, ">", Resources.getMessage("DialogQuery.51"), ">", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, relationalOperators, ">=", Resources.getMessage("DialogQuery.55"), ">=", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
Group fields = new Group(composite, SWT.SHADOW_ETCHED_IN);
fields.setText(Resources.getMessage("DialogQuery.58")); //$NON-NLS-1$
fields.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
fields.setLayout(GridLayoutFactory.swtDefaults().numColumns(1).create());
Combo combo = new Combo(fields, SWT.SINGLE | SWT.READ_ONLY | SWT.DROP_DOWN);
for (int i = 0; i < data.getHandle().getNumColumns(); i++) {
combo.add(data.getHandle().getAttributeName(i));
}
combo.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
combo.select(0);
createButton(text, combo, fields, Resources.getMessage("DialogQuery.59"), Resources.getMessage("DialogQuery.60"), "", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
createButton(text, null, fields, Resources.getMessage("DialogQuery.63"), Resources.getMessage("DialogQuery.64"), "'value'", "", 1, false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
Group actions = new Group(composite, SWT.SHADOW_ETCHED_IN);
actions.setText(Resources.getMessage("DialogQuery.67")); //$NON-NLS-1$
actions.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
actions.setLayout(GridLayoutFactory.swtDefaults().numColumns(1).create());
Button select = new Button(actions, SWT.PUSH);
select.setText(Resources.getMessage("DialogQuery.68")); //$NON-NLS-1$
select.setToolTipText(Resources.getMessage("DialogQuery.69")); //$NON-NLS-1$
select.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
select.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent arg0) {
text.selectAll();
updateButtons();
}
});
Button clear = new Button(actions, SWT.PUSH);
clear.setText(Resources.getMessage("DialogQuery.70")); //$NON-NLS-1$
clear.setToolTipText(Resources.getMessage("DialogQuery.71")); //$NON-NLS-1$
clear.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
clear.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent arg0) {
Point selection = text.getSelectionRange();
StringBuilder builder = new StringBuilder();
if (selection.y != 0) {
builder.append(text.getTextRange(0, selection.x));
builder.append(text.getTextRange(selection.x + selection.y, text.getText().length() - (selection.x + selection.y)));
text.setText(builder.toString());
highlight();
parse();
updateButtons();
}
}
});
status = new Label(parent, SWT.NONE);
status.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).span(2, 1).create());
status.setText(""); //$NON-NLS-1$
highlight();
new Thread(updater).start();
return parent;
}
@Override
protected ShellListener getShellListener() {
return new ShellAdapter() {
@Override
public void shellClosed(final ShellEvent event) {
setReturnCode(Window.CANCEL);
}
};
}
@Override
protected boolean isResizable() {
return false;
}
}