package org.jbpm.ui.dialog;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
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.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
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.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.HyperlinkGroup;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.jbpm.ui.DesignerLogger;
import org.jbpm.ui.PluginConstants;
import org.jbpm.ui.common.model.ProcessDefinition;
import org.jbpm.ui.resource.Messages;
import org.jbpm.ui.util.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXParseException;
public class SQLActionHandlerConfigDialog extends Dialog {
private static final List<String> PREDEFINED_VARIABLES = new ArrayList<String>();
static {
PREDEFINED_VARIABLES.add("instanceId");
PREDEFINED_VARIABLES.add("currentDate");
}
private TabFolder tabFolder;
private XmlContentView xmlContentView;
private ConstructorView constructorView;
private final ProcessDefinition definition;
private final String initialValue;
private SQLTasksModel model;
private String result;
public SQLActionHandlerConfigDialog(ProcessDefinition definition, String initialValue) {
super(Display.getCurrent().getActiveShell());
setShellStyle(getShellStyle() | SWT.RESIZE);
this.definition = definition;
this.initialValue = initialValue;
}
@Override
protected Point getInitialSize() {
return new Point(600, 400);
}
@Override
protected Control createDialogArea(Composite parent) {
getShell().setText(Messages.getString("SQLActionHandlerConfig.title"));
tabFolder = new TabFolder(parent, SWT.BORDER);
tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH));
Composite composite = new Composite(tabFolder, SWT.NONE);
composite.setLayout(new GridLayout());
TabItem tabItem1 = new TabItem(tabFolder, SWT.NONE);
tabItem1.setText(Messages.getString("SQLActionHandlerConfig.title.configuration"));
tabItem1.setControl(composite);
ScrolledComposite scrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL | SWT.BORDER);
scrolledComposite.setExpandHorizontal(true);
scrolledComposite.setExpandVertical(true);
scrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
try {
if (initialValue.trim().length() != 0) {
model = SQLTasksModel.fromXml(initialValue);
}
} catch (Exception ex) {
DesignerLogger.logError(Messages.getString("SQLActionHandlerConfig.error.parse"), ex);
// TODO translate to = Unable to parse model from XML
}
if (model == null) {
model = SQLTasksModel.createDefault();
}
constructorView = new ConstructorView(scrolledComposite, SWT.NONE);
constructorView.setLayoutData(new GridData(GridData.FILL_BOTH));
model.getFirstTask().changeListener = constructorView;
scrolledComposite.setContent(constructorView);
xmlContentView = new XmlContentView(tabFolder, SWT.NONE);
xmlContentView.setLayoutData(new GridData(GridData.FILL_BOTH));
xmlContentView.setValidateXSD(true);
xmlContentView.setValue(initialValue);
TabItem tabItem2 = new TabItem(tabFolder, SWT.NONE);
tabItem2.setText(" XML ");
tabItem2.setControl(xmlContentView);
tabFolder.addSelectionListener(new TabSelectionHandler());
return tabFolder;
}
@Override
protected void okPressed() {
if(tabFolder.getSelectionIndex() == 0) {
populateToSourceView();
}
if (xmlContentView.validate()) {
this.result = xmlContentView.getValue();
super.okPressed();
}
}
private void populateToConstructorView() {
try {
String xml = xmlContentView.getValue();
model = SQLTasksModel.fromXml(xml);
model.getFirstTask().changeListener = constructorView;
constructorView.buildFromModel();
} catch (Exception ex) {
DesignerLogger.logError("Unable to parse model from XML", ex);
}
}
private void populateToSourceView() {
String xml = model.toXml();
xmlContentView.setValue(xml);
}
public String getResult() {
return result;
}
private class TabSelectionHandler extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
if (tabFolder.getSelectionIndex() == 0) {
if (xmlContentView.validate()) {
populateToConstructorView();
} else {
// return to source view due to invalid xml
tabFolder.setSelection(1);
}
} else {
populateToSourceView();
}
}
}
private class ConstructorView extends Composite implements ChangeListener {
private HyperlinkGroup hyperlinkGroup = new HyperlinkGroup(Display.getCurrent());
public ConstructorView(Composite parent, int style) {
super(parent, style);
setLayout(new GridLayout(3, false));
buildFromModel();
}
public void stateChanged() {
buildFromModel();
}
public void buildFromModel() {
try {
for (Control control : getChildren()) {
control.dispose();
}
if (model.tasks.size() > 0) {
addTaskSection(model.tasks.get(0));
}
((ScrolledComposite) getParent()).setMinSize(computeSize(getSize().x, SWT.DEFAULT));
this.layout(true, true);
} catch (Throwable e) {
DesignerLogger.logErrorWithoutDialog("Cannot build model", e);
}
}
private void addTaskSection(SQLTaskModel taskModel) {
Label label = new Label(this, SWT.NONE);
label.setText(Messages.getString("label.DataSourceName"));
final Text text = new Text(this, SWT.BORDER);
text.setText(taskModel.dsName);
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent event) {
model.getFirstTask().dsName = text.getText();
}
});
GridData data = new GridData();
data.widthHint = 200;
text.setLayoutData(data);
Hyperlink hl = new Hyperlink(this, SWT.NONE);
hl.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_END));
hl.setText(Messages.getString("button.add") + " " + Messages.getString("label.SQLQuery"));
hl.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
model.getFirstTask().addQuery();
}
});
hyperlinkGroup.add(hl);
for (SQLQueryModel queryModel : taskModel.queries) {
addQuerySection(queryModel, taskModel.queries.indexOf(queryModel));
}
}
private void addQuerySection(SQLQueryModel queryModel, final int queryIndex) {
Group group = new Group(this, SWT.NONE);
group.setData(queryIndex);
group.setText(Messages.getString("label.SQLQuery"));
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 3;
group.setLayoutData(data);
group.setLayout(new GridLayout(2, false));
final Text text = new Text(group, SWT.BORDER);
text.setText(queryModel.query);
text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent event) {
model.getFirstTask().queries.get(queryIndex).query = text.getText();
}
});
Hyperlink hl1 = new Hyperlink(group, SWT.NONE);
hl1.setText("[X]");
hl1.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
model.getFirstTask().deleteQuery(queryIndex);
}
});
hyperlinkGroup.add(hl1);
Composite paramsComposite = createParametersComposite(group, "label.SQLParams", queryIndex, false);
for (SQLQueryParameterModel parameterModel : queryModel.params) {
addParamSection(paramsComposite, parameterModel, queryIndex, queryModel.params.indexOf(parameterModel), true);
}
Composite resultsComposite = createParametersComposite(group, "label.SQLResults", queryIndex, true);
for (SQLQueryParameterModel parameterModel : queryModel.results) {
addParamSection(resultsComposite, parameterModel, queryIndex, queryModel.results.indexOf(parameterModel), false);
}
}
private Composite createParametersComposite(Composite parent, String labelKey, final int queryIndex, final boolean result) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(3, false));
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 3;
composite.setLayoutData(data);
Composite strokeComposite = new Composite(composite, SWT.NONE);
data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 3;
strokeComposite.setLayoutData(data);
strokeComposite.setLayout(new GridLayout(4, false));
Label strokeLabel = new Label(strokeComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
data = new GridData();
data.widthHint = 50;
strokeLabel.setLayoutData(data);
Label headerLabel = new Label(strokeComposite, SWT.NONE);
headerLabel.setText(Messages.getString(labelKey));
strokeLabel = new Label(strokeComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
strokeLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Hyperlink hl2 = new Hyperlink(strokeComposite, SWT.NONE);
hl2.setText(Messages.getString("button.add"));
hl2.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
model.getFirstTask().addQueryParameter(queryIndex, result);
}
});
hyperlinkGroup.add(hl2);
return composite;
}
private void addParamSection(Composite parent, final SQLQueryParameterModel parameterModel, final int queryIndex, final int paramIndex, boolean input) {
final Combo combo = new Combo(parent, SWT.READ_ONLY);
List<String> vars = definition.getVariableNames(true);
for (String variableName : vars) {
combo.add(variableName);
}
if (input) {
for (String variableName : PREDEFINED_VARIABLES) {
combo.add(variableName);
}
}
combo.setText(parameterModel.varName);
combo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
combo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
parameterModel.varName = combo.getText();
parameterModel.swimlaneVar = definition.getSwimlaneByName(parameterModel.varName) != null;
}
});
if (paramIndex != 0) {
Hyperlink hl0 = new Hyperlink(parent, SWT.NONE);
hl0.setText(Messages.getString("button.up"));
hl0.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
model.getFirstTask().moveUpQueryParameter(queryIndex, parameterModel.result, paramIndex);
}
});
hyperlinkGroup.add(hl0);
} else {
new Label(parent, SWT.NONE);
}
Hyperlink hl1 = new Hyperlink(parent, SWT.NONE);
hl1.setText("[X]");
hl1.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
model.getFirstTask().deleteQueryParameter(queryIndex, parameterModel.result, paramIndex);
}
});
hyperlinkGroup.add(hl1);
}
}
public static class XmlContentView extends Composite {
private StyledText styledText;
private Label errorLabel;
private boolean validateXSD;
public XmlContentView(Composite parent, int style) {
super(parent, style);
setLayout(new GridLayout());
errorLabel = new Label(this, SWT.NONE);
errorLabel.setForeground(ColorConstants.red);
errorLabel.setText("");
errorLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
styledText = new StyledText(this, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
styledText.setLayoutData(new GridData(GridData.FILL_BOTH));
XmlHighlightTextStyling lineStyleListener = new XmlHighlightTextStyling();
styledText.addLineStyleListener(lineStyleListener);
styledText.setLineSpacing(2);
}
public void setValidateXSD(boolean validateXSD) {
this.validateXSD = validateXSD;
}
private void setErrorLabelText(String text) {
errorLabel.setText(text);
errorLabel.pack(true);
}
public boolean validate() {
try {
InputStream xmlStream = new ByteArrayInputStream(styledText.getText().getBytes(PluginConstants.UTF_ENCODING));
if (validateXSD) {
XmlUtil.parseDocumentValidateXSD(xmlStream);
} else {
XmlUtil.parseDocument(xmlStream);
}
return true;
} catch (SAXParseException e) {
int lineNumber = ((SAXParseException) e).getLineNumber();
int offset = ((SAXParseException) e).getColumnNumber();
if (lineNumber > 1) {
styledText.setLineBackground(lineNumber-1, 1, new Color(null, 200, 200, 200));
}
setErrorLabelText(e.getMessage() + " [" + lineNumber + ", " + offset + "]");
} catch (Exception e) {
setErrorLabelText(e.getMessage());
}
return false;
}
public void setValue(String value) {
styledText.setText(value);
}
public String getValue() {
return styledText.getText();
}
}
private interface ChangeListener {
void stateChanged();
}
public static class SQLTasksModel {
public List<SQLTaskModel> tasks = new ArrayList<SQLTaskModel>();
private SQLTasksModel() {}
public static SQLTasksModel createDefault() {
SQLTasksModel model = new SQLTasksModel();
model.tasks.add(new SQLTaskModel());
return model;
}
public String toXml() {
try {
Document document = XmlUtil.createDocument("database-tasks", "database-tasks.xsd");
for (SQLTaskModel model : tasks) {
model.serialize(document, document.getDocumentElement());
}
return new String(XmlUtil.writeXml(document) , PluginConstants.UTF_ENCODING);
} catch (Exception e) {
throw new RuntimeException("Unable serialize model to XML", e);
}
}
public static SQLTasksModel fromXml(String xml) throws Exception {
SQLTasksModel model = new SQLTasksModel();
Document document = XmlUtil.parseDocument(new ByteArrayInputStream(xml.getBytes(PluginConstants.UTF_ENCODING)));
NodeList taskElements = document.getElementsByTagName("task");
for (int i = 0; i < taskElements.getLength(); i++) {
Element taskElement = (Element) taskElements.item(i);
SQLTaskModel taskModel = SQLTaskModel.deserialize(taskElement);
model.tasks.add(taskModel);
}
return model;
}
public SQLTaskModel getFirstTask() {
return tasks.get(0);
}
}
static class SQLTaskModel {
public String dsName = "";
public List<SQLQueryModel> queries = new ArrayList<SQLQueryModel>();
public ChangeListener changeListener;
public void deleteQuery(int index) {
queries.remove(index);
changeListener.stateChanged();
}
public void addQuery() {
queries.add(new SQLQueryModel());
changeListener.stateChanged();
}
public void deleteQueryParameter(int index, boolean result, int paramIndex) {
SQLQueryModel queryModel = queries.get(index);
if (result) {
queryModel.results.remove(paramIndex);
} else {
queryModel.params.remove(paramIndex);
}
changeListener.stateChanged();
}
public void addQueryParameter(int index, boolean result) {
SQLQueryModel queryModel = queries.get(index);
if (result) {
queryModel.results.add(new SQLQueryParameterModel(result));
} else {
queryModel.params.add(new SQLQueryParameterModel(result));
}
changeListener.stateChanged();
}
public void moveUpQueryParameter(int index, boolean result, int paramIndex) {
SQLQueryModel queryModel = queries.get(index);
if (result) {
Collections.swap(queryModel.results, paramIndex-1, paramIndex);
} else {
Collections.swap(queryModel.params, paramIndex-1, paramIndex);
}
changeListener.stateChanged();
}
public void serialize(Document document, Element parent) throws Exception {
Element taskElement = document.createElement("task");
taskElement.setAttribute("datasource", dsName);
parent.appendChild(taskElement);
Element queriesElement = document.createElement("queries");
for (SQLQueryModel model : queries) {
model.serialize(document, queriesElement);
}
taskElement.appendChild(queriesElement);
}
public static SQLTaskModel deserialize(Element element) throws Exception {
SQLTaskModel model = new SQLTaskModel();
model.dsName = element.getAttribute("datasource");
Element queriesElement = (Element) element.getElementsByTagName("queries").item(0);
NodeList queryElements = queriesElement.getElementsByTagName("query");
for (int i = 0; i < queryElements.getLength(); i++) {
SQLQueryModel queryModel = SQLQueryModel.deserialize((Element) queryElements.item(i));
model.queries.add(queryModel);
}
return model;
}
}
static class SQLQueryModel {
public String query = "";
public List<SQLQueryParameterModel> params = new ArrayList<SQLQueryParameterModel>();
public List<SQLQueryParameterModel> results = new ArrayList<SQLQueryParameterModel>();
public void serialize(Document document, Element parent) {
Element queryElement = document.createElement("query");
queryElement.setAttribute("sql", query);
parent.appendChild(queryElement);
for (SQLQueryParameterModel model : params) {
model.serialize(document, queryElement);
}
for (SQLQueryParameterModel model : results) {
model.serialize(document, queryElement);
}
}
public static SQLQueryModel deserialize(Element element) throws Exception {
SQLQueryModel model = new SQLQueryModel();
model.query = element.getAttribute("sql");
NodeList childs = element.getChildNodes();
for (int i = 0; i < childs.getLength(); i++) {
Node child = childs.item(i);
if (child instanceof Element) {
SQLQueryParameterModel parameterModel = SQLQueryParameterModel.deserialize((Element) child);
if (parameterModel.result) {
model.results.add(parameterModel);
} else {
model.params.add(parameterModel);
}
}
}
return model;
}
}
static class SQLQueryParameterModel {
public boolean result;
public boolean swimlaneVar;
public String varName = "";
public SQLQueryParameterModel() {
}
public SQLQueryParameterModel(boolean result) {
this.result = result;
}
public void serialize(Document document, Element parent) {
String elementName;
if (result) {
elementName = swimlaneVar ? "swimlane-result" : "result";
} else {
elementName = swimlaneVar ? "swimlane-param" : "param";
}
Element paramElement = document.createElement(elementName);
paramElement.setAttribute("var", varName);
if (swimlaneVar) {
paramElement.setAttribute("field", "code");
}
parent.appendChild(paramElement);
}
public static SQLQueryParameterModel deserialize(Element element) throws Exception {
SQLQueryParameterModel model = new SQLQueryParameterModel();
String elementName = element.getNodeName();
if ("swimlane-result".equals(elementName)) {
model.result = true;
model.swimlaneVar = true;
}
if ("result".equals(elementName)) {
model.result = true;
model.swimlaneVar = false;
}
if ("swimlane-param".equals(elementName)) {
model.result = false;
model.swimlaneVar = true;
}
if ("param".equals(elementName)) {
model.result = false;
model.swimlaneVar = false;
}
model.varName = element.getAttribute("var");
return model;
}
}
}