/*
* Copyright 2017 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.dtable.client.wizard.table.pages;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import org.drools.workbench.models.datamodel.oracle.DataType;
import org.drools.workbench.models.datamodel.oracle.ModelField;
import org.drools.workbench.models.datamodel.rule.BaseSingleFieldConstraint;
import org.drools.workbench.models.guided.dtable.shared.model.ActionCol52;
import org.drools.workbench.models.guided.dtable.shared.model.ActionSetFieldCol52;
import org.drools.workbench.models.guided.dtable.shared.model.DTCellValue52;
import org.drools.workbench.models.guided.dtable.shared.model.GuidedDecisionTable52;
import org.drools.workbench.models.guided.dtable.shared.model.Pattern52;
import org.drools.workbench.screens.guided.dtable.client.resources.i18n.GuidedDecisionTableConstants;
import org.drools.workbench.screens.guided.dtable.client.widget.DTCellValueWidgetFactory;
import org.drools.workbench.screens.guided.dtable.client.wizard.table.pages.events.ActionSetFieldsDefinedEvent;
import org.drools.workbench.screens.guided.dtable.client.wizard.table.pages.events.DuplicatePatternsEvent;
import org.drools.workbench.screens.guided.dtable.client.wizard.table.pages.events.PatternRemovedEvent;
import org.kie.workbench.common.widgets.client.widget.HumanReadableDataTypes;
import org.uberfire.client.callbacks.Callback;
import org.uberfire.ext.widgets.core.client.wizards.WizardPageStatusChangeEvent;
/**
* A page for the guided Decision Table Wizard to define Actions setting fields
* on previously bound patterns. This page does not use the GuidedDecisionTable
* model directly; instead maintaining its own Pattern-to-Action associations.
*/
@Dependent
public class ActionSetFieldsPage extends AbstractGuidedDecisionTableWizardPage
implements
ActionSetFieldsPageView.Presenter {
@Inject
private ActionSetFieldsPageView view;
@Inject
private Event<ActionSetFieldsDefinedEvent> actionSetFieldsDefinedEvent;
@Inject
private Event<WizardPageStatusChangeEvent> wizardPageStatusChangeEvent;
//GuidedDecisionTable52 maintains a single collection of Actions, linked to patterns by boundName. Thus if multiple
//patterns are bound to the same name we cannot distinguish which Actions relate to which Patterns. The Wizard therefore
//maintains it's own internal association of Patterns to Actions. IdentityHashMap is used as it is possible to have two
//identically defined Patterns (i.e. they have the same property values) although they represent different instances.
//A WeakIdentityHashMap would have been more appropriate, however JavaScript has no concept of a weak reference, and so
//it can't be implement in GWT. In the absence of such a Map an Event is raised by FactPatternsPage when a Pattern is
//removed that is handled here to synchronise the Pattern lists.
private Map<Pattern52, List<ActionSetFieldCol52>> patternToActionsMap = new IdentityHashMap<Pattern52, List<ActionSetFieldCol52>>();
@Override
public String getTitle() {
return GuidedDecisionTableConstants.INSTANCE.DecisionTableWizardActionSetFields();
}
@Override
public void initialise() {
view.init( this );
view.setValidator( getValidator() );
patternToActionsMap.clear();
//Set-up validator for the pattern-to-action mapping voodoo
getValidator().setPatternToActionSetFieldsMap( patternToActionsMap );
//Set-up a factory for value editors
view.setDTCellValueWidgetFactory( DTCellValueWidgetFactory.getInstance( model,
oracle,
false,
allowEmptyValues() ) );
//Existing ActionSetFieldCols (should be empty for a new Decision Table)
for ( ActionCol52 a : model.getActionCols() ) {
if ( a instanceof ActionSetFieldCol52 ) {
final ActionSetFieldCol52 asf = (ActionSetFieldCol52) a;
final Pattern52 p = model.getConditionPattern( asf.getBoundName() );
if ( !patternToActionsMap.containsKey( p ) ) {
patternToActionsMap.put( p,
new ArrayList<ActionSetFieldCol52>() );
}
final List<ActionSetFieldCol52> actions = patternToActionsMap.get( p );
actions.add( asf );
}
}
view.setChosenFields( new ArrayList<ActionSetFieldCol52>() );
content.setWidget( view );
}
@Override
public void prepareView() {
//Setup the available patterns, that could have changed each time this page is visited
List<Pattern52> availablePatterns = new ArrayList<Pattern52>();
for ( Pattern52 p : model.getPatterns() ) {
if ( p.getChildColumns().size() > 0 ) {
availablePatterns.add( p );
} else {
patternToActionsMap.remove( p );
}
}
view.setAvailablePatterns( availablePatterns );
}
@Override
public void isComplete( final Callback<Boolean> callback ) {
//Have all Actions been defined?
boolean areActionSetFieldsDefined = true;
for ( List<ActionSetFieldCol52> actions : patternToActionsMap.values() ) {
for ( ActionSetFieldCol52 a : actions ) {
if ( !getValidator().isActionValid( a ) ) {
areActionSetFieldsDefined = false;
break;
}
}
}
//Signal Action Set Fields definitions to other pages
final ActionSetFieldsDefinedEvent event = new ActionSetFieldsDefinedEvent( areActionSetFieldsDefined );
actionSetFieldsDefinedEvent.fire( event );
callback.callback( areActionSetFieldsDefined );
}
//See comments about use of IdentityHashMap in instance member declaration section
public void onPatternRemoved( final @Observes PatternRemovedEvent event ) {
patternToActionsMap.remove( event.getPattern() );
}
public void onDuplicatePatterns( final @Observes DuplicatePatternsEvent event ) {
view.setArePatternBindingsUnique( event.getArePatternBindingsUnique() );
}
public void onActionSetFieldsDefined( final @Observes ActionSetFieldsDefinedEvent event ) {
view.setAreActionSetFieldsDefined( event.getAreActionSetFieldsDefined() );
}
@Override
public void selectPattern( final Pattern52 pattern ) {
//Pattern is null when programmatically deselecting an item
if ( pattern == null ) {
return;
}
//Add fields available
final String type = pattern.getFactType();
oracle.getFieldCompletions( type,
new Callback<ModelField[]>() {
@Override
public void callback( final ModelField[] fields ) {
final List<AvailableField> availableFields = new ArrayList<AvailableField>();
for ( ModelField modelField : fields ) {
final String fieldName = modelField.getName();
final String fieldType = oracle.getFieldType( type,
fieldName );
final String fieldDisplayType = HumanReadableDataTypes.getUserFriendlyTypeName( fieldType );
final AvailableField field = new AvailableField( fieldName,
fieldType,
fieldDisplayType,
BaseSingleFieldConstraint.TYPE_LITERAL );
availableFields.add( field );
}
view.setAvailableFields( availableFields );
//Set fields already chosen
List<ActionSetFieldCol52> actionsForPattern = patternToActionsMap.get( pattern );
if ( actionsForPattern == null ) {
actionsForPattern = new ArrayList<ActionSetFieldCol52>();
patternToActionsMap.put( pattern,
actionsForPattern );
}
view.setChosenFields( actionsForPattern );
}
} );
}
@Override
public void makeResult( final GuidedDecisionTable52 model ) {
//Copy actions to decision table model. Assertion of bindings occurs in FactPatternsPage
for ( Map.Entry<Pattern52, List<ActionSetFieldCol52>> ps : patternToActionsMap.entrySet() ) {
final Pattern52 p = ps.getKey();
//Patterns with no conditions don't get created
if ( p.getChildColumns().size() > 0 ) {
final String binding = p.getBoundName();
for ( ActionSetFieldCol52 a : ps.getValue() ) {
a.setBoundName( binding );
model.getActionCols().add( a );
}
}
}
}
@Override
public GuidedDecisionTable52.TableFormat getTableFormat() {
return model.getTableFormat();
}
@Override
public boolean hasEnums( final ActionSetFieldCol52 selectedAction ) {
for ( Map.Entry<Pattern52, List<ActionSetFieldCol52>> e : this.patternToActionsMap.entrySet() ) {
if ( e.getValue().contains( selectedAction ) ) {
final String factType = e.getKey().getFactType();
final String factField = selectedAction.getFactField();
return this.oracle.hasEnums( factType,
factField );
}
}
return false;
}
@Override
public void assertDefaultValue( final Pattern52 selectedPattern,
final ActionSetFieldCol52 selectedAction ) {
final List<String> valueList = Arrays.asList( columnUtilities.getValueList( selectedAction ) );
if ( valueList.size() > 0 ) {
final String defaultValue = cellUtilities.asString( selectedAction.getDefaultValue() );
if ( !valueList.contains( defaultValue ) ) {
selectedAction.getDefaultValue().clearValues();
}
} else {
//Ensure the Default Value has been updated to represent the column's data-type.
final DTCellValue52 defaultValue = selectedAction.getDefaultValue();
final DataType.DataTypes dataType = columnUtilities.getDataType( selectedPattern,
selectedAction );
cellUtilities.convertDTCellValueType( dataType,
defaultValue );
}
}
@Override
public void stateChanged() {
final WizardPageStatusChangeEvent event = new WizardPageStatusChangeEvent( this );
wizardPageStatusChangeEvent.fire( event );
}
}