/** * 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.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. * * @author Ahti Kitsik * @author Anton Arhipov */ 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.sentence ); 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 ); } protected void updateSentence() { String newSentence = ""; for ( Iterator iter = widgets.iterator(); iter.hasNext(); ) { ModelWidget wid = (ModelWidget) iter.next(); if ( wid instanceof LabelWidget ) { newSentence = newSentence + ((LabelWidget) wid).getText(); } else if ( wid instanceof FieldEditor ) { FieldEditor editor = (FieldEditor) wid; String varString = editor.getText(); String restriction = editor.getRestriction(); if ( !restriction.equals( "" ) ) { varString = varString + ":" + restriction; } newSentence = newSentence + " {" + varString + "} "; } else if ( wid instanceof DSLDropDown ) { // Add the meta-data back to the field so that is shows up as a // dropdown when refreshed from repo DSLDropDown drop = (DSLDropDown) wid; Combo box = (Combo) drop.getControl(); String type = drop.getType(); String factAndField = drop.getFactAndField(); String key = ITEM_ + (box.getSelectionIndex() + 1); Object keyval = box.getData( key ); newSentence = newSentence + "{" + keyval + ":" + type + ":" + factAndField + "} "; } else if ( wid instanceof DSLCheckBox ) { DSLCheckBox check = (DSLCheckBox) wid; String checkValue = check.getCheckedValue(); newSentence = newSentence + "{" + checkValue + ":" + check.getType() + ":" + checkValue + "} "; } else if ( wid instanceof DSLDateSelector ) { DSLDateSelector dateSel = (DSLDateSelector) wid; String dateString = dateSel.getDateString(); String format = dateSel.getJavascriptFormat(); newSentence = newSentence + "{" + dateString + ":" + dateSel.getType() + ":" + format + "} "; } else if ( wid instanceof NewLine ) { newSentence = newSentence + "\\n"; } } this.sentence.sentence = newSentence.trim(); } 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; } public String getRestriction() { return this.regex; } public boolean isValid() { boolean result = true; if ( !regex.equals( "" ) ) result = this.control.getText().matches( this.regex ); return result; } } 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 getType() { return type; } public void setType(String type) { this.type = type; } public String getFactAndField() { return factAndField; } public void setFactAndField(String factAndField) { this.factAndField = factAndField; } } 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 getType() { return DATE_TAG; } public String getJavascriptFormat() { return this.javascriptFormat; } 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; } public String getVarName() { return varName; } public void setVarName(String varName) { this.varName = varName; } } }