/*
* Copyright 2010 JBoss Inc
*
* 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.drools.eclipse.rulebuilder.ui;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.drools.core.util.DateUtils;
import org.drools.ide.common.client.modeldriven.SuggestionCompletionEngine;
import org.drools.ide.common.client.modeldriven.brl.DSLSentence;
import org.drools.ide.common.client.modeldriven.brl.DSLVariableValue;
import org.drools.ide.common.client.modeldriven.ui.ConstraintValueEditorHelper;
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.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
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.DateTime;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ImageHyperlink;
/**
* This displays a widget to edit a DSL sentence.
*/
public abstract class DSLSentenceWidget extends Widget {
private static final String ENUM_TAG = "ENUM";
private static final String DATE_TAG = "DATE";
private static final String BOOLEAN_TAG = "BOOLEAN";
private static final String ITEM_ = "ITEM_";
private final DSLSentence sentence;
protected SuggestionCompletionEngine completions;
private List<ModelWidget> widgets = new ArrayList<ModelWidget>();
public DSLSentenceWidget(FormToolkit toolkit,
Composite parent,
DSLSentence sentence,
RuleModeller modeller,
int index) {
super( parent,
toolkit,
modeller,
index );
this.sentence = sentence;
completions = modeller.getSuggestionCompletionEngine();
Composite lastRow = makeWidgets( this.sentence.getDefinition() );
addDeleteAction( lastRow );
toolkit.paintBordersFor( parent );
}
protected abstract void updateModel();
private void addDeleteAction(Composite parent) {
ImageHyperlink delLink = addImage( parent,
"icons/delete_item_small.gif" );
delLink.addHyperlinkListener( new IHyperlinkListener() {
public void linkActivated(HyperlinkEvent e) {
MessageBox dialog = new MessageBox( Display.getCurrent().getActiveShell(),
SWT.YES | SWT.NO | SWT.ICON_WARNING );
dialog.setMessage( "Remove this DSL sentence?" );
dialog.setText( "Remove this DSL sentence?" );
if ( dialog.open() == SWT.YES ) {
updateModel();
}
}
public void linkEntered(HyperlinkEvent e) {
}
public void linkExited(HyperlinkEvent e) {
}
} );
delLink.setToolTipText( "Remove this condition." );
}
/**
* This will take a DSL line item, and split it into widget thingamies for
* displaying. One day, if this is too complex, this will have to be done on
* the server side.
*
* @return
*/
public Composite makeWidgets(String dslLine) {
List<ModelWidget> lineWidgets = new ArrayList<ModelWidget>();
int startVariable = dslLine.indexOf( "{" );
boolean firstOneIsBracket = (dslLine.indexOf( "{" ) == 0);
String startLabel = "";
if ( startVariable > 0 ) {
startLabel = dslLine.substring( 0,
startVariable );
} else if ( !firstOneIsBracket ) {
// There are no curly brackets in the text.
// Just print it
startLabel = dslLine;
}
parent.setLayout( new GridLayout( 1,
true ) );
Composite row = toolkit.createComposite( parent );
RowLayout rl = new RowLayout();
rl.marginBottom = 0;
rl.marginHeight = 0;
rl.marginLeft = 0;
rl.marginRight = 0;
rl.pack = true;
rl.spacing = 0;
rl.center = true;
//rl.fill=true;
row.setLayout( rl );
lineWidgets.add( new LabelWidget( row,
startLabel ) );
while ( startVariable > 0 || firstOneIsBracket ) {
firstOneIsBracket = false;
int endVariable = dslLine.indexOf( "}",
startVariable );
String currVariable = dslLine.substring( startVariable + 1,
endVariable );
lineWidgets.add( addVariable( row,
currVariable ) );
// Parse out the next label between variables
startVariable = dslLine.indexOf( "{",
endVariable );
String lbl;
if ( startVariable > 0 ) {
lbl = dslLine.substring( endVariable + 1,
startVariable );
} else {
lbl = dslLine.substring( endVariable + 1,
dslLine.length() );
}
if ( lbl.indexOf( "\\n" ) > -1 ) {
String[] lines = lbl.split( "\\\\n" );
for ( int i = 0; i < lines.length; i++ ) {
row = toolkit.createComposite( parent );
row.setLayout( rl );
lineWidgets.add( new DSLSentenceWidget.NewLine( row ) );
lineWidgets.add( new LabelWidget( row,
lines[i] ) );
}
} else {
lineWidgets.add( new LabelWidget( row,
lbl ) );
}
}
for ( ModelWidget widg : lineWidgets ) {
widgets.add( widg );
}
updateSentence();
return row;
}
public ModelWidget addVariable(Composite parent,
String currVariable) {
// Formats are: <varName>:ENUM:<Field.type>
// <varName>:DATE:<dateFormat>
// <varName>:BOOLEAN:[checked | unchecked] <-initial value
int colonIndex = currVariable.indexOf( ":" );
if ( colonIndex > 0 ) {
String definition = currVariable.substring( colonIndex + 1,
currVariable.length() );
int secondColonIndex = definition.indexOf( ":" );
if ( secondColonIndex > 0 ) {
String type = currVariable.substring( colonIndex + 1,
colonIndex + secondColonIndex + 1 );
if ( type.equalsIgnoreCase( ENUM_TAG ) ) {
return addEnumDropdown( parent,
currVariable );
} else if ( type.equalsIgnoreCase( DATE_TAG ) ) {
return addDateSelector( parent,
currVariable );
} else if ( type.equalsIgnoreCase( BOOLEAN_TAG ) ) {
return addCheckbox( parent,
currVariable );
}
} else {
String regex = currVariable.substring( colonIndex + 1,
currVariable.length() );
return addBox( parent,
currVariable,
regex );
}
}
return addBox( parent,
currVariable,
"" );
}
public ModelWidget addBox(Composite parent,
String variableDef,
String regex) {
int colonIndex = variableDef.indexOf( ":" );
if ( colonIndex > 0 ) {
variableDef = variableDef.substring( 0,
colonIndex );
}
FieldEditor currentBox = new FieldEditor( parent );
currentBox.setVisibleLength( variableDef.length() + 1 );
currentBox.setText( variableDef );
currentBox.setRestriction( regex );
return currentBox;
}
public ModelWidget addCheckbox(Composite parent,
String variableDef) {
return new DSLCheckBox( parent,
variableDef );
}
public ModelWidget addDateSelector(Composite parent,
String variableDef) {
return new DSLDateSelector( parent,
variableDef );
}
private ModelWidget addEnumDropdown(Composite parent,
String variableDef) {
return new DSLDropDown( parent,
variableDef );
}
/**
* This will go through the widgets and extract the values
*/
protected void updateSentence() {
int iVariable = 0;
for ( Iterator<ModelWidget> iter = widgets.iterator(); iter.hasNext(); ) {
ModelWidget wid = (ModelWidget) iter.next();
if ( wid instanceof FieldEditor ) {
FieldEditor editor = (FieldEditor) wid;
sentence.getValues().set( iVariable++,
new DSLVariableValue(editor.getText().trim()) );
} else if ( wid instanceof DSLDropDown ) {
DSLDropDown drop = (DSLDropDown) wid;
sentence.getValues().set( iVariable++,
new DSLVariableValue(drop.getSelectedValue()) );
} else if ( wid instanceof DSLCheckBox ) {
DSLCheckBox check = (DSLCheckBox) wid;
sentence.getValues().set( iVariable++,
new DSLVariableValue(check.getCheckedValue().trim()) );
} else if ( wid instanceof DSLDateSelector ) {
DSLDateSelector dateSel = (DSLDateSelector) wid;
String dateString = dateSel.getDateString();
sentence.getValues().set( iVariable++,
new DSLVariableValue(dateString) );
}
}
}
class LabelWidget
implements
ModelWidget {
private Label control;
private String val;
public LabelWidget(Composite parent,
String text) {
val = text;
control = toolkit.createLabel( parent,
text );
}
public Control getControl() {
return control;
}
public String getText() {
return val;
}
}
class NewLine
implements
ModelWidget {
private Composite control;
public NewLine(Composite parent) {
control = toolkit.createComposite( parent );
}
public Control getControl() {
return control;
}
}
class FieldEditor
implements
ModelWidget {
private Text control;
private String oldValue = "";
private String regex = "";
public FieldEditor(Composite parent) {
control = toolkit.createText( parent,
"" );
// box.setStyleName( "dsl-field-TextBox" );
control.addModifyListener( new ModifyListener() {
public void modifyText(ModifyEvent e) {
Text otherBox = control;
if ( !regex.equals( "" ) && !otherBox.getText().matches( regex ) ) {
System.err.println( "VALUE IS NOT VALID: " + otherBox.getText() );
control.setText( oldValue );
} else {
oldValue = otherBox.getText();
updateSentence();
getModeller().setDirty( true );
}
}
} );
}
public void setText(String t) {
control.setText( t );
}
public void setVisibleLength(int l) {
//box.set
//TODO Implement!
}
public Control getControl() {
return control;
}
public String getText() {
return control.getText();
}
public void setRestriction(String regex) {
this.regex = regex;
}
}
class DSLDropDown
implements
ModelWidget {
Combo resultWidget = null;
// Format for the dropdown def is <varName>:<type>:<Fact.field>
private String varName = "";
private String type = "";
private String factAndField = "";
public DSLDropDown(Composite parent,
String variableDef) {
Composite comp = toolkit.createComposite( parent );
comp.setLayout( new FillLayout() );
int firstIndex = variableDef.indexOf( ":" );
int lastIndex = variableDef.lastIndexOf( ":" );
varName = variableDef.substring( 0,
firstIndex );
type = variableDef.substring( firstIndex + 1,
lastIndex );
factAndField = variableDef.substring( lastIndex + 1,
variableDef.length() );
int dotIndex = factAndField.indexOf( "." );
String type = factAndField.substring( 0,
dotIndex );
String field = factAndField.substring( dotIndex + 1,
factAndField.length() );
String[] data = completions.getEnumValues( type,
field );
Combo list = new Combo( comp,
SWT.DROP_DOWN | SWT.READ_ONLY );
if ( data != null ) {
int selected = 0; // Select first item for now by default. Null values are not allowed.
for ( int i = 0; i < data.length; i++ ) {
String realValue = data[i];
String display = data[i];
if ( data[i].indexOf( '=' ) > -1 ) {
String[] vs = ConstraintValueEditorHelper.splitValue( data[i] );
realValue = vs[0];
display = vs[1];
}
if ( varName.equals( realValue ) ) {
selected = i;
}
list.add( display );
String key = ITEM_ + list.getItemCount();
list.setData( key,
realValue );
}
if ( selected >= 0 ) list.select( selected );
}
list.addModifyListener( new ModifyListener() {
public void modifyText(ModifyEvent e) {
updateSentence();
getModeller().setDirty( true );
}
} );
resultWidget = list;
comp.layout();
}
public Control getControl() {
return resultWidget;
}
public String getSelectedValue() {
return resultWidget.getText();
}
}
class DSLCheckBox
implements
ModelWidget {
Combo resultWidget = null;
// Format for the dropdown def is <varName>:<type>:<Fact.field>
private String varName = "";
private Composite control;
public DSLCheckBox(Composite parent,
String variableDef) {
control = toolkit.createComposite( parent );
control.setLayout( new RowLayout() );
int firstIndex = variableDef.indexOf( ":" );
int lastIndex = variableDef.lastIndexOf( ":" );
varName = variableDef.substring( 0,
firstIndex );
String checkedUnchecked = variableDef.substring( lastIndex + 1,
variableDef.length() );
resultWidget = new Combo( control,
SWT.READ_ONLY );
resultWidget.add( "true" );
resultWidget.add( "false" );
if ( checkedUnchecked.equalsIgnoreCase( "true" ) ) {
resultWidget.select( 0 );
} else {
resultWidget.select( 1 );
}
resultWidget.addModifyListener( new ModifyListener() {
public void modifyText(ModifyEvent e) {
updateSentence();
modeller.setDirty( true );
}
} );
resultWidget.setVisible( true );
control.layout();
}
public Control getControl() {
return control;
}
public Combo getListBox() {
return resultWidget;
}
public void setListBox(Combo resultWidget) {
this.resultWidget = resultWidget;
}
public String getType() {
return BOOLEAN_TAG;
}
public String getVarName() {
return varName;
}
public void setVarName(String varName) {
this.varName = varName;
}
public String getCheckedValue() {
return this.resultWidget.getSelectionIndex() == 0 ? "true" : "false";
}
}
class DSLDateSelector
implements
ModelWidget {
//DateTime resultWidget = null;
// Format for the dropdown def is <varName>:<type>:<Fact.field>
private String varName = "";
private String javascriptFormat = "";
private final String defaultJavascriptFormat = "d-M-y";
private final String javaFormat = DateUtils.getDateFormatMask();
private SimpleDateFormat formatter = null;
private Composite control;
private Text field;
public DSLDateSelector(final Composite parent,
String variableDef) {
control = toolkit.createComposite( parent );
control.setLayout( new RowLayout() );
int firstIndex = variableDef.indexOf( ":" );
int lastIndex = variableDef.lastIndexOf( ":" );
varName = variableDef.substring( 0,
firstIndex );
javascriptFormat = variableDef.substring( lastIndex + 1,
variableDef.length() );
// Resolve the javascript format
if ( javascriptFormat.equals( "" ) || javascriptFormat.equals( "default" ) ) {
javascriptFormat = defaultJavascriptFormat;
}
// Set the java format for formatter
formatter = new SimpleDateFormat( javaFormat );
Date origDate = new Date();
if ( !varName.equals( "" ) ) {
try {
origDate = formatter.parse( varName );
} catch ( Exception e ) {
e.printStackTrace();
}
}
field = toolkit.createText( control,
"" );
final Button open = new Button( control,
SWT.ARROW | SWT.DOWN );
//open.setText ("Set");
if ( origDate != null ) field.setText( formatter.format( origDate ) );
field.addModifyListener( new ModifyListener() {
public void modifyText(ModifyEvent e) {
updateSentence();
modeller.setDirty( true );
}
} );
open.addSelectionListener( new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
final Shell dialog = new Shell( open.getShell(),
SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL );
dialog.setText( "Set date:" );
dialog.setLayout( new GridLayout( 1,
false ) );
final DateTime calendar = new DateTime( dialog,
SWT.CALENDAR );
Date date = new Date();
try {
String txt = field.getText();
if ( txt != null && txt.length() > 0 ) {
date = formatter.parse( txt );
}
} catch ( ParseException e1 ) {
e1.printStackTrace();
}
calendar.setDate( date.getYear() + 1900,
date.getMonth(),
date.getDate() );
Point p2 = open.toDisplay( 0,
0 );
int x = p2.x;
int y = p2.y + 20;
dialog.setLocation( x,
y );
Button ok = new Button( dialog,
SWT.PUSH );
ok.setText( "OK" );
ok.setLayoutData( new GridData( SWT.FILL,
SWT.CENTER,
false,
false ) );
ok.addSelectionListener( new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
field.setText( formatter.format( new Date( calendar.getYear() - 1900,
calendar.getMonth(),
calendar.getDay() ) ) );
updateSentence();
modeller.setDirty( true );
dialog.close();
}
} );
dialog.setDefaultButton( ok );
dialog.pack();
dialog.open();
}
} );
control.layout();
}
public Control getControl() {
return control;
}
public String getDateString() {
Date value = new Date();
try {
String txt = field.getText();
if ( txt != null && txt.length() > 0 ) {
value = formatter.parse( txt );
}
} catch ( ParseException e ) {
e.printStackTrace();
return "";
}
String result = "";
if ( value != null ) result = formatter.format( value );
else result = varName;
return result;
}
}
}