/* * 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.guvnor.client.modeldriven.ui; import static org.drools.ide.common.client.modeldriven.brl.ExpressionPartHelper.getExpressionPartForField; import static org.drools.ide.common.client.modeldriven.brl.ExpressionPartHelper.getExpressionPartForGlobalVariable; import static org.drools.ide.common.client.modeldriven.brl.ExpressionPartHelper.getExpressionPartForMethod; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.drools.guvnor.client.common.ClickableLabel; import org.drools.guvnor.client.common.FormStylePopup; import org.drools.guvnor.client.common.SmallLabel; import org.drools.guvnor.client.messages.Constants; import org.drools.guvnor.client.util.Format; import org.drools.ide.common.client.modeldriven.SuggestionCompletionEngine; import org.drools.ide.common.client.modeldriven.brl.ExpressionCollectionIndex; import org.drools.ide.common.client.modeldriven.brl.ExpressionFieldVariable; import org.drools.ide.common.client.modeldriven.brl.ExpressionFormLine; import org.drools.ide.common.client.modeldriven.brl.ExpressionMethod; import org.drools.ide.common.client.modeldriven.brl.ExpressionPart; import org.drools.ide.common.client.modeldriven.brl.ExpressionText; import org.drools.ide.common.client.modeldriven.brl.ExpressionVariable; import org.drools.ide.common.client.modeldriven.brl.FactPattern; import org.drools.ide.common.client.modeldriven.brl.RuleModel; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; public class ExpressionBuilder extends RuleModellerWidget implements HasExpressionTypeChangeHandlers, HasExpressionChangeHandlers { private static final String DELETE_VALUE = "_delete_"; private static final String FIElD_VALUE_PREFIX = "fl"; private static final String VARIABLE_VALUE_PREFIX = "va"; // private static final String GLOBAL_COLLECTION_VALUE_PREFIX = "gc"; private static final String GLOBAL_VARIABLE_VALUE_PREFIX = "gv"; private static final String METHOD_VALUE_PREFIX = "mt"; private final SmallLabelClickHandler slch = new SmallLabelClickHandler(); private Constants constants = ((Constants) GWT.create( Constants.class )); // private FlowPanel panel = new FlowPanel(); private HorizontalPanel panel = new HorizontalPanel(); private ExpressionFormLine expression; private boolean readOnly; public ExpressionBuilder(RuleModeller modeller, ExpressionFormLine expression) { this( modeller, expression, false ); } public ExpressionBuilder(RuleModeller modeller, ExpressionFormLine expression, Boolean readOnly) { super( modeller ); if ( readOnly == null ) { this.readOnly = !modeller.getSuggestionCompletions().containsFactType( modeller.getSuggestionCompletions().getFactNameFromType( this.expression.getRootExpression().getClassType() ) ); } else { this.readOnly = readOnly; } panel.setVerticalAlignment( HasVerticalAlignment.ALIGN_MIDDLE ); this.expression = expression; if ( expression == null || expression.isEmpty() ) { if ( this.readOnly ) { panel.add( new SmallLabel( "<b>-</b>" ) ); } else { panel.add( createStartPointWidget() ); } } else { if ( this.readOnly ) { panel.add( createWidgetForExpression( "<b>" + getBoundText() + expression.getText( false ) + "</b>" ) ); } else { panel.add( createWidgetForExpression( "<b>" + getBoundText() + expression.getText( false ) + ".</b>" ) ); panel.add( getWidgetForCurrentType() ); } } initWidget( panel ); } private String getBoundText() { if ( expression.isBound() ) { return "[" + expression.getBinding() + "] "; } return ""; } private Widget createStartPointWidget() { ListBox startPoint = new ListBox(); panel.add( startPoint ); startPoint.addItem( constants.ChooseDotDotDot(), "" ); // TODO {baunax} uncomment when global collections is implemented. // for (String gc : getCompletionEngine().getGlobalCollections()) { // startPoint.addItem(gc, GLOBAL_COLLECTION_VALUE_PREFIX + "." + gc); // } for ( String gv : getCompletionEngine().getGlobalVariables() ) { startPoint.addItem( gv, GLOBAL_VARIABLE_VALUE_PREFIX + "." + gv ); } for ( String v : getRuleModel().getBoundFacts() ) { startPoint.addItem( v, VARIABLE_VALUE_PREFIX + "." + v ); } startPoint.setVisibleItemCount( 1 ); startPoint.addChangeHandler( new ChangeHandler() { public void onChange(ChangeEvent event) { ListBox lb = (ListBox) event.getSource(); int index = lb.getSelectedIndex(); if ( index > 0 ) { ExpressionBuilder.this.makeDirty(); startPointChange( lb.getValue( index ) ); } } } ); return startPoint; } @Override public void makeDirty() { super.makeDirty(); setModified( true ); } private void startPointChange(String value) { setModified( true ); panel.clear(); Widget w; int dotPos = value.indexOf( '.' ); String prefix = value.substring( 0, dotPos ); String attrib = value.substring( dotPos + 1 ); if ( prefix.equals( VARIABLE_VALUE_PREFIX ) ) { FactPattern fact = getRuleModel().getBoundFact( attrib ); ExpressionPart variable; if ( fact != null ) { variable = new ExpressionVariable( fact ); } else { //TODO {baunax} fix it!!! to make recursive variable = new ExpressionFieldVariable( attrib ); } expression.appendPart( variable ); } else if ( prefix.equals( GLOBAL_VARIABLE_VALUE_PREFIX ) ) { expression.appendPart( getExpressionPartForGlobalVariable( getCompletionEngine(), attrib ) ); } w = getWidgetForCurrentType(); if ( !expression.isEmpty() ) { panel.add( createWidgetForExpression( "<b>" + expression.getText() + ".</b>" ) ); } if ( w != null ) { panel.add( w ); } fireExpressionChangeEvent(); fireExpressionTypeChangeEvent(); } private Widget getWidgetForCurrentType() { if ( expression.isEmpty() ) { return createStartPointWidget(); } ChangeHandler ch = new ChangeHandler() { public void onChange(ChangeEvent event) { ListBox box = (ListBox) event.getSource(); panel.remove( box ); if ( box.getSelectedIndex() > 0 ) { onChangeSelection( box.getValue( box.getSelectedIndex() ) ); } } }; ListBox lb = new ListBox(); lb.setVisibleItemCount( 1 ); lb.addItem( constants.ChooseDotDotDot(), "" ); lb.addItem( "<==" + constants.DeleteItem(), DELETE_VALUE ); for ( Map.Entry<String, String> entry : getCompletionsForCurrentType().entrySet() ) { lb.addItem( entry.getKey(), entry.getValue() ); } lb.addChangeHandler( ch ); return lb; } private void onCollectionChange(String value) { if ( "size".contains( value ) ) { expression.appendPart( new ExpressionMethod( "size", "int", SuggestionCompletionEngine.TYPE_NUMERIC ) ); } else if ( "isEmpty".equals( value ) ) { expression.appendPart( new ExpressionMethod( "isEmpty", "boolean", SuggestionCompletionEngine.TYPE_BOOLEAN ) ); } else { ExpressionCollectionIndex collectionIndex; String factName = getCompletionEngine().getFactNameFromType( getCurrentParametricType() ); if ( getCurrentParametricType() != null && factName != null ) { collectionIndex = new ExpressionCollectionIndex( "get", getCurrentParametricType(), factName ); } else { collectionIndex = new ExpressionCollectionIndex( "get", "java.lang.Object", SuggestionCompletionEngine.TYPE_OBJECT ); } if ( "first".equals( value ) ) { collectionIndex.putParam( "index", new ExpressionFormLine( new ExpressionText( "0" ) ) ); expression.appendPart( collectionIndex ); } else if ( "last".equals( value ) ) { ExpressionFormLine index = new ExpressionFormLine( expression ); index.appendPart( new ExpressionMethod( "size", "int", SuggestionCompletionEngine.TYPE_NUMERIC ) ); index.appendPart( new ExpressionText( "-1" ) ); collectionIndex.putParam( "index", index ); expression.appendPart( collectionIndex ); } } } private void onChangeSelection(String value) { setModified( true ); String oldType = getCurrentGenericType(); String prevFactName = null; if ( DELETE_VALUE.equals( value ) ) { expression.removeLast(); } else if ( SuggestionCompletionEngine.TYPE_COLLECTION.equals( getCurrentGenericType() ) ) { onCollectionChange( value ); } else if ( SuggestionCompletionEngine.TYPE_STRING.equals( getCurrentGenericType() ) ) { if ( "size".equals( value ) ) { expression.appendPart( new ExpressionMethod( "size", "int", SuggestionCompletionEngine.TYPE_NUMERIC ) ); } else if ( "isEmpty".equals( value ) ) { expression.appendPart( new ExpressionText( ".size() == 0", "", SuggestionCompletionEngine.TYPE_NUMERIC ) ); } } else { int dotPos = value.indexOf( '.' ); String prefix = value.substring( 0, dotPos ); String attrib = value.substring( dotPos + 1 ); prevFactName = getCompletionEngine().getFactNameFromType( getCurrentClassType() ); // String genericType = SuggestionCompletionEngine.TYPE_OBJECT; if ( FIElD_VALUE_PREFIX.equals( prefix ) ) { expression.appendPart( getExpressionPartForField( getCompletionEngine(), prevFactName, attrib ) ); } else if ( METHOD_VALUE_PREFIX.equals( prefix ) ) { expression.appendPart( getExpressionPartForMethod( getCompletionEngine(), prevFactName, attrib ) ); } } Widget w = getWidgetForCurrentType(); panel.clear(); if ( !expression.isEmpty() ) { panel.add( createWidgetForExpression( "<b>" + expression.getText() + ".</b>" ) ); } if ( w != null ) { panel.add( w ); } fireExpressionChangeEvent(); fireExpressionTypeChangeEvent( oldType ); } private Map<String, String> getCompletionsForCurrentType() { Map<String, String> completions = new LinkedHashMap<String, String>(); if ( SuggestionCompletionEngine.TYPE_FINAL_OBJECT.equals( getCurrentGenericType() ) ) { return completions; } if ( SuggestionCompletionEngine.TYPE_COLLECTION.equals( getCurrentGenericType() ) ) { completions.put( "size()", "size" ); completions.put( "first()", "first" ); completions.put( "last()", "last" ); completions.put( "isEmpty()", "isEmpty" ); return completions; } if ( SuggestionCompletionEngine.TYPE_STRING.equals( getCurrentGenericType() ) ) { completions.put( "size()", "size" ); completions.put( "isEmpty()", "isEmpty" ); return completions; } if ( SuggestionCompletionEngine.TYPE_BOOLEAN.equals( getCurrentGenericType() ) || SuggestionCompletionEngine.TYPE_NUMERIC.equals( getCurrentGenericType() ) || SuggestionCompletionEngine.TYPE_DATE.equals( getCurrentGenericType() ) || SuggestionCompletionEngine.TYPE_OBJECT.equals( getCurrentGenericType() ) ) { return completions; } String factName = getCompletionEngine().getFactNameFromType( getCurrentClassType() ); if ( factName != null ) { // we currently only support 0 param method calls List<String> methodNames = getCompletionEngine().getMethodFullNames( factName, 0 ); for ( String field : getCompletionEngine().getFieldCompletions( factName ) ) { boolean changed = false; for ( Iterator<String> i = methodNames.iterator(); i.hasNext(); ) { String method = i.next(); if ( method.startsWith( field ) ) { completions.put( method, METHOD_VALUE_PREFIX + "." + method ); i.remove(); changed = true; } } if ( !changed ) { completions.put( field, FIElD_VALUE_PREFIX + "." + field ); } } } // else {We don't know anything about this type, so return empty map} return completions; } // private String getCurrentPartName() { // return expression.getCurrentName(); // } private RuleModel getRuleModel() { return this.getModeller().getModel(); } private SuggestionCompletionEngine getCompletionEngine() { return this.getModeller().getSuggestionCompletions(); } private String getCurrentClassType() { return expression.getClassType(); } private String getCurrentGenericType() { return expression.getGenericType(); } private String getPreviousGenericType() { return expression.getPreviousGenericType(); } private String getCurrentParametricType() { return expression.getParametricType(); } // private String getPreviousClassType() { // return expression.getPreviousType(); // } // // private ExpressionPart getRootExpression() { // return expression.getRootExpression(); // } @Override public boolean isReadOnly() { return this.readOnly; } /** * @see org.drools.guvnor.client.modeldriven.ui.HasExpressionTypeChangeHandlers#addExpressionTypeChangeHandler(org.drools.guvnor.client.modeldriven.ui.ExpressionTypeChangeHandler) */ public HandlerRegistration addExpressionTypeChangeHandler(ExpressionTypeChangeHandler handler) { return addHandler( handler, ExpressionTypeChangeEvent.getType() ); } private void fireExpressionChangeEvent() { fireEvent( new ExpressionChangeEvent() ); } private void fireExpressionTypeChangeEvent() { fireExpressionTypeChangeEvent( getPreviousGenericType() ); } private void fireExpressionTypeChangeEvent(String previousGenericType) { String currentGenericType = getCurrentGenericType(); if ( (previousGenericType == null || !previousGenericType.equals( currentGenericType )) || currentGenericType != null ) { fireEvent( new ExpressionTypeChangeEvent( previousGenericType, currentGenericType ) ); } } public HandlerRegistration addExpressionChangeHandler(ExpressionChangeHandler handler) { return addHandler( handler, ExpressionChangeEvent.getType() ); } private void showBindingPopUp() { final FormStylePopup popup = new FormStylePopup(); popup.setWidth( 500 + "px" ); HorizontalPanel vn = new HorizontalPanel(); final TextBox varName = new TextBox(); Button ok = new Button( constants.Set() ); vn.add( new Label( constants.BindTheExpressionToAVariable() ) ); vn.add( varName ); vn.add( ok ); ok.addClickHandler( new ClickHandler() { public void onClick(ClickEvent event) { String var = varName.getText(); if ( getModeller().isVariableNameUsed( var ) ) { Window.alert( Format.format( constants.TheVariableName0IsAlreadyTaken(), var ) ); return; } expression.setBinding( var ); getModeller().refreshWidget(); popup.hide(); } } ); popup.addRow( vn ); popup.show(); } private class SmallLabelClickHandler implements ClickHandler { public void onClick(ClickEvent event) { showBindingPopUp(); } } private ClickableLabel createWidgetForExpression(String text) { ClickableLabel label = new ClickableLabel( text, slch ); return label; } }