/*
* Copyright 2014 Red Hat, Inc. and/or its affiliates.
*
* 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.workbench.screens.guided.dtree.client.widget.popups;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import org.drools.workbench.models.datamodel.oracle.OperatorsOracle;
import org.drools.workbench.models.guided.dtree.shared.model.nodes.ConstraintNode;
import org.drools.workbench.models.guided.dtree.shared.model.nodes.Node;
import org.drools.workbench.models.guided.dtree.shared.model.nodes.impl.ConstraintNodeImpl;
import org.drools.workbench.screens.guided.dtree.client.resources.i18n.GuidedDecisionTreeConstants;
import org.drools.workbench.screens.guided.dtree.client.widget.utils.BindingUtilities;
import org.gwtbootstrap3.client.ui.FormGroup;
import org.gwtbootstrap3.client.ui.HelpBlock;
import org.gwtbootstrap3.client.ui.Label;
import org.gwtbootstrap3.client.ui.ListBox;
import org.gwtbootstrap3.client.ui.constants.ValidationState;
import org.kie.workbench.common.widgets.client.datamodel.AsyncPackageDataModelOracle;
import org.kie.workbench.common.widgets.client.resources.HumanReadable;
import org.uberfire.client.callbacks.Callback;
import org.uberfire.ext.widgets.common.client.common.popups.BaseModal;
import org.uberfire.ext.widgets.common.client.common.popups.footers.ModalFooterOKCancelButtons;
public class EditConstraintPopup extends BaseModal {
interface EditConstraintBinder
extends
UiBinder<Widget, EditConstraintPopup> {
}
private static EditConstraintBinder uiBinder = GWT.create( EditConstraintBinder.class );
private final ConstraintNode node;
private final ConstraintNode clone;
private final Command callback;
private final AsyncPackageDataModelOracle oracle;
private final ValueEditorFactory valueEditorFactory = new ValueEditorFactory() {
@Override
protected Map<String, String> getCurrentValueMap() {
//Get a Map<String(FieldName), String(FieldValue)> of all Constraints proceeding the one being edited
//Dependent enumerations span a single Pattern so only walk the tree up to the first non-ConstraintNode
//as this represents the boundary between Patterns.
final Map<String, String> currentValueMap = new HashMap<String, String>();
Node parent = node.getParent();
while ( parent != null ) {
if ( parent instanceof ConstraintNode ) {
final ConstraintNode cn = (ConstraintNode) parent;
currentValueMap.put( cn.getFieldName(),
cn.getValue().toString() );
parent = parent.getParent();
} else {
parent = null;
}
}
return currentValueMap;
}
};
private final Command okCommand = new Command() {
@Override
public void execute() {
onOKButtonClick();
}
};
private final Command cancelCommand = new Command() {
@Override
public void execute() {
hide();
}
};
private final ModalFooterOKCancelButtons footer = new ModalFooterOKCancelButtons( okCommand,
cancelCommand );
@UiField
Label classNameLabel;
@UiField
Label fieldNameLabel;
@UiField
FormGroup bindingGroup;
@UiField
HelpBlock bindingHelpInline;
@UiField
BindingTextBox bindingTextBox;
@UiField
ListBox operatorListBox;
@UiField
FormGroup valueGroup;
@UiField
SimplePanel valueHolder;
/**
* Edit the given ConstraintNode. A clone is taken whilst editing is in progress to preserve the state
* of the original node should editing be cancelled by the User. Bindings are checked to be unique
* in the path from this node being edited to the tree's root. When the User commits the changes
* the provided callback is executed and this popup closed.
* @param node The node to edit
* @param oracle DataModelOracle to drive population of popup
* @param callback Callback to execute when the User commits changes
*/
public EditConstraintPopup( final ConstraintNode node,
final AsyncPackageDataModelOracle oracle,
final Command callback ) {
setTitle( GuidedDecisionTreeConstants.INSTANCE.popupTitleEditConstraint() );
setBody( uiBinder.createAndBindUi( this ) );
add( footer );
bindingTextBox.addKeyPressHandler( new KeyPressHandler() {
@Override
public void onKeyPress( final KeyPressEvent event ) {
bindingGroup.setValidationState( ValidationState.NONE );
bindingHelpInline.setText( "" );
}
} );
bindingTextBox.addBlurHandler( new BlurHandler() {
@Override
public void onBlur( final BlurEvent event ) {
clone.setBinding( bindingTextBox.getText() );
}
} );
this.node = node;
this.clone = cloneNode( node );
this.oracle = oracle;
this.callback = callback;
this.classNameLabel.setText( clone.getClassName() );
this.fieldNameLabel.setText( clone.getFieldName() );
this.bindingTextBox.setText( clone.getBinding() );
initialiseOperators();
}
//Clone node whilst editing to preserve original node should User cancel the edit
private ConstraintNode cloneNode( final ConstraintNode node ) {
final ConstraintNode clone = new ConstraintNodeImpl( node.getClassName(),
node.getFieldName() );
if ( node.getOperator() != null ) {
clone.setOperator( node.getOperator() );
}
if ( node.getValue() != null ) {
clone.setValue( node.getValue() );
}
clone.setParent( node.getParent() );
clone.setBinding( node.getBinding() );
return clone;
}
private void initialiseOperators() {
oracle.getOperatorCompletions( clone.getClassName(),
clone.getFieldName(),
new Callback<String[]>() {
@Override
public void callback( final String[] operators ) {
operatorListBox.clear();
operatorListBox.setEnabled( false );
if ( operators == null ) {
return;
}
int selectedIndex = 0;
operatorListBox.setEnabled( true );
operatorListBox.addItem( GuidedDecisionTreeConstants.INSTANCE.noOperator() );
for ( int index = 0; index < operators.length; index++ ) {
final String operator = operators[ index ];
if ( operator.equals( clone.getOperator() ) ) {
selectedIndex = index + 1;
}
operatorListBox.addItem( HumanReadable.getOperatorDisplayName( operator ),
operator );
}
operatorListBox.setSelectedIndex( selectedIndex );
initialiseValue();
}
} );
operatorListBox.addChangeHandler( new ChangeHandler() {
@Override
public void onChange( final ChangeEvent event ) {
final int selectedIndex = operatorListBox.getSelectedIndex();
if ( selectedIndex == 0 ) {
clone.setOperator( null );
} else {
clone.setOperator( operatorListBox.getValue( selectedIndex ) );
}
initialiseValue();
}
} );
}
private void initialiseValue() {
//Don't show a ValueEditor if no operator has been selected
if ( operatorListBox.getSelectedIndex() == 0 ) {
clone.setValue( null );
valueGroup.setVisible( false );
valueHolder.clear();
return;
}
//Don't show a ValueEditor if the operator does not require a Value
final String operator = operatorListBox.getValue( operatorListBox.getSelectedIndex() );
if ( !OperatorsOracle.isValueRequired( operator ) ) {
clone.setValue( null );
valueGroup.setVisible( false );
valueHolder.clear();
return;
}
//Get an editor for the class, field and operator
final Widget editor = valueEditorFactory.getValueEditor( clone.getClassName(),
clone.getFieldName(),
clone,
oracle,
OperatorsOracle.operatorRequiresList( operator ) );
valueHolder.clear();
if ( editor != null ) {
valueGroup.setVisible( true );
valueHolder.setWidget( editor );
}
}
private void onOKButtonClick() {
boolean hasError = false;
final String binding = clone.getBinding();
if ( !( binding == null || binding.isEmpty() ) ) {
if ( !BindingUtilities.isUniqueInPath( binding,
clone ) ) {
bindingGroup.setValidationState( ValidationState.ERROR );
bindingHelpInline.setText( GuidedDecisionTreeConstants.INSTANCE.bindingIsNotUnique() );
hasError = true;
}
} else {
bindingGroup.setValidationState( ValidationState.NONE );
}
if ( hasError ) {
return;
}
//Copy changes into the original node
node.setBinding( clone.getBinding() );
node.setOperator( clone.getOperator() );
node.setValue( clone.getValue() );
if ( callback != null ) {
callback.execute();
}
hide();
}
}