Package org.drools.workbench.screens.guided.dtable.client.wizard.pages

Source Code of org.drools.workbench.screens.guided.dtable.client.wizard.pages.RowExpander$ColumnDynamicValues

/*
* Copyright 2011 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.workbench.screens.guided.dtable.client.wizard.pages;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.drools.workbench.models.datamodel.oracle.DropDownData;
import org.drools.workbench.models.guided.dtable.shared.model.ActionCol52;
import org.drools.workbench.models.guided.dtable.shared.model.ActionInsertFactCol52;
import org.drools.workbench.models.guided.dtable.shared.model.ActionSetFieldCol52;
import org.drools.workbench.models.guided.dtable.shared.model.BaseColumn;
import org.drools.workbench.models.guided.dtable.shared.model.ConditionCol52;
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.widget.table.LimitedEntryDropDownManager;
import org.drools.workbench.screens.guided.dtable.client.utils.GuidedDecisionTableUtils;
import org.kie.workbench.common.widgets.client.datamodel.AsyncPackageDataModelOracle;
import org.kie.workbench.common.widgets.client.util.ConstraintValueEditorHelper;

/**
* A utility class to expand Condition column definitions into rows. Action
* columns are not expanded, as the use-case is that a user-determined action
* should be specified for each combination of Conditons. Where a column is
* defined as having multiple values (Guvnor enum, Java enum or Decision Table
* Value List) the number of rows is the Cartesian Product of all combinations.
*/
public class RowExpander {

    private Map<BaseColumn, ColumnValues> expandedColumns = new IdentityHashMap<BaseColumn, ColumnValues>();
    private List<ColumnValues> columns;

    private final GuidedDecisionTable52 model;
    private final GuidedDecisionTableUtils modelUtils;
    private final AsyncPackageDataModelOracle oracle;

    private static final List<DTCellValue52> EMPTY_VALUE = new ArrayList<DTCellValue52>();

    {
        EMPTY_VALUE.add( new DTCellValue52() );
    }

    /**
     * Constructor
     * @param model
     * @param oracle
     */
    public RowExpander( final GuidedDecisionTable52 model,
                        final AsyncPackageDataModelOracle oracle ) {
        this.columns = new ArrayList<ColumnValues>();
        this.model = model;
        this.oracle = oracle;
        this.modelUtils = new GuidedDecisionTableUtils( model,
                                                        oracle );

        //Add all columns to Expander to generate row data. The AnalysisCol is not added
        //as its data is transient, not held in the underlying Decision Table's data
        addRowNumberColumn();
        addRowDescriptionColumn();
        addConditionColumns();
        addActionColumns();
    }

    public List<ColumnValues> getColumns() {
        return this.columns;
    }

    private void addRowNumberColumn() {
        ColumnValues cv = new RowNumberColumnValues( columns,
                                                     EMPTY_VALUE,
                                                     new DTCellValue52() );
        cv.setExpandColumn( false );
        this.expandedColumns.put( model.getRowNumberCol(),
                                  cv );
        this.columns.add( cv );
    }

    private void addRowDescriptionColumn() {
        ColumnValues cv = new RowDescriptionColumnValues( columns,
                                                          EMPTY_VALUE,
                                                          new DTCellValue52() );
        cv.setExpandColumn( false );
        this.expandedColumns.put( model.getDescriptionCol(),
                                  cv );
        this.columns.add( cv );
    }

    private void addConditionColumns() {
        for ( Pattern52 p : model.getPatterns() ) {
            addColumn( p );
        }
    }

    private void addActionColumns() {
        for ( ActionCol52 a : model.getActionCols() ) {
            if ( a instanceof ActionSetFieldCol52 ) {
                ActionSetFieldCol52 afc = (ActionSetFieldCol52) a;
                addColumn( afc );
            } else if ( a instanceof ActionInsertFactCol52 ) {
                ActionInsertFactCol52 aif = (ActionInsertFactCol52) a;
                addColumn( aif );
            }
        }
    }

    private void addColumn( final Pattern52 p ) {
        for ( ConditionCol52 c : p.getChildColumns() ) {
            addColumn( p,
                       c );
        }
    }

    private void addColumn( final Pattern52 p,
                            final ConditionCol52 c ) {
        switch ( model.getTableFormat() ) {
            case EXTENDED_ENTRY:
                addExtendedEntryColumn( p,
                                        c );
                break;
            case LIMITED_ENTRY:
                addLimitedEntryColumn( c );
                break;
        }
    }

    private void addExtendedEntryColumn( final Pattern52 p,
                                         final ConditionCol52 c ) {
        ColumnValues cv = null;
        String[] values = new String[]{ };
        if ( modelUtils.hasValueList( c ) ) {
            values = modelUtils.getValueList( c );
            values = getSplitValues( values );
            cv = new ColumnValues( columns,
                                   convertValueList( values ),
                                   c.getDefaultValue() );

        } else if ( oracle.hasEnums( p.getFactType(),
                                     c.getFactField() ) ) {
            final LimitedEntryDropDownManager.Context context = new LimitedEntryDropDownManager.Context( p,
                                                                                                         c );
            cv = new ColumnDynamicValues( columns,
                                          oracle,
                                          context,
                                          c.getDefaultValue() );

        } else {
            cv = new ColumnValues( columns,
                                   convertValueList( values ),
                                   c.getDefaultValue() );
        }

        if ( cv != null ) {
            this.expandedColumns.put( c,
                                      cv );
            this.columns.add( cv );
        }
    }

    private void addLimitedEntryColumn( final ConditionCol52 c ) {
        final List<DTCellValue52> values = new ArrayList<DTCellValue52>();
        values.add( new DTCellValue52( Boolean.TRUE ) );
        values.add( new DTCellValue52( Boolean.FALSE ) );

        final ColumnValues cv = new ColumnValues( columns,
                                                  values,
                                                  c.getDefaultValue() );
        this.expandedColumns.put( c,
                                  cv );
        this.columns.add( cv );
    }

    private String[] getSplitValues( final String[] values ) {
        final String[] splitValues = new String[ values.length ];
        for ( int i = 0; i < values.length; i++ ) {
            String v = values[ i ];
            String[] splut = ConstraintValueEditorHelper.splitValue( v );
            splitValues[ i ] = splut[ 0 ];
        }
        return splitValues;
    }

    private static List<DTCellValue52> convertValueList( final String[] values ) {
        final List<DTCellValue52> convertedValues = new ArrayList<DTCellValue52>();
        for ( String value : values ) {
            convertedValues.add( new DTCellValue52( value ) );
        }
        return convertedValues;
    }

    private void addColumn( final ActionSetFieldCol52 a ) {
        final ColumnValues cv = new ColumnValues( columns,
                                                  EMPTY_VALUE,
                                                  model.getTableFormat() == GuidedDecisionTable52.TableFormat.EXTENDED_ENTRY ? a.getDefaultValue() : new DTCellValue52( Boolean.FALSE ) );
        cv.setExpandColumn( false );
        this.expandedColumns.put( a,
                                  cv );
        this.columns.add( cv );
    }

    private void addColumn( final ActionInsertFactCol52 a ) {
        final ColumnValues cv = new ColumnValues( columns,
                                                  EMPTY_VALUE,
                                                  model.getTableFormat() == GuidedDecisionTable52.TableFormat.EXTENDED_ENTRY ? a.getDefaultValue() : new DTCellValue52( Boolean.FALSE ) );
        cv.setExpandColumn( false );
        this.expandedColumns.put( a,
                                  cv );
        this.columns.add( cv );
    }

    /**
     * Rather than return a List of rows as the expanded form we expose an
     * Iterator with which the expanded form can be retrieved. This decision was
     * to avoid potentially hugh transient Lists being created; as the results
     * from this class will be transformed into other representations.
     * @return
     */
    public RowIterator iterator() {
        return new RowIterator();
    }

    /**
     * Indicate whether the provided column should be expanded or not. If the
     * column was not part of the Decision Table used in the Constructor no
     * action is taken.
     * @param column
     * @param expandColumn
     */
    public void setExpandColumn( final BaseColumn column,
                                 final boolean expandColumn ) {
        final ColumnValues cv = this.expandedColumns.get( column );
        if ( cv == null ) {
            return;
        }
        cv.setExpandColumn( expandColumn );
    }

    /**
     * An iterator that retrieves the expanded rows one at a time
     */
    public class RowIterator
            implements
            Iterator<List<DTCellValue52>> {

        //Check if all columns have had their value lists consumed
        @Override
        public boolean hasNext() {
            for ( ColumnValues cv : columns ) {
                if ( !cv.isAllValuesUsed() ) {
                    return true;
                }
            }
            return false;
        }

        //Build a row from the columns current values and advance the first column. Columns
        //check whether all their values have been used and advance the subsequent column
        //so a ripple effect can be observed, with one column advancing the next, which
        //advances the next and so on...
        @Override
        public List<DTCellValue52> next() {

            //We have a row that is potentially partially populated as the dependent enum data has not been set
            //So ask columns to update their value lists based on the current row definition. This will force
            //the dependent enumeration value lists to be populated.
            boolean refreshRow = false;
            List<DTCellValue52> row;
            do {
                refreshRow = false;
                row = new ArrayList<DTCellValue52>();
                for ( ColumnValues cv : columns ) {
                    row.add( cv.getCurrentValue() );
                }
                for ( ColumnValues cv : columns ) {
                    if ( cv instanceof ColumnDynamicValues ) {
                        final ColumnDynamicValues cdv = (ColumnDynamicValues) cv;
                        refreshRow = refreshRow || cdv.assertValueList( row );
                    }
                }
            } while ( refreshRow );

            //Advance the first column to the next value
            columns.get( columns.size() - 1 ).advanceColumnValue();
            return row;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException( "remove is not supported on RowIterator" );
        }

    }

    /**
     * Container for a columns values
     */
    static class ColumnValues {

        List<DTCellValue52> values;
        List<DTCellValue52> originalValues;
        List<ColumnValues> columns;
        DTCellValue52 value;
        DTCellValue52 defaultValue;
        Iterator<DTCellValue52> iterator;
        boolean expandColumn = true;
        boolean isAllValuesUsed = false;

        ColumnValues( final List<ColumnValues> columns,
                      final List<DTCellValue52> values,
                      final DTCellValue52 defaultValue ) {
            this.columns = columns;
            this.defaultValue = defaultValue;
            this.values = values;
            this.originalValues = this.values;

            //If no values were provided add the default and record that all values have been used
            if ( this.values.size() == 0 ) {
                this.values = new ArrayList<DTCellValue52>();
                this.values.add( defaultValue );
                this.originalValues = this.values;
                this.isAllValuesUsed = true;
            }

            //Initialise value to the first in the list
            this.iterator = this.values.iterator();
            this.value = iterator.next();
        }

        void setExpandColumn( final boolean expandColumn ) {
            this.expandColumn = expandColumn;
            if ( expandColumn ) {
                this.values = this.originalValues;
                this.isAllValuesUsed = false;
            } else {
                this.values = new ArrayList<DTCellValue52>();
                this.values.add( defaultValue );
                this.isAllValuesUsed = true;
            }
            //Initialise value to the first in the list
            this.iterator = this.values.iterator();
            this.value = iterator.next();
        }

        /**
         * Get the current value of the column
         * @return
         */
        DTCellValue52 getCurrentValue() {
            return this.value;
        }

        /**
         * Advance to the next value for the column, resetting to the beginning
         * of the list if all values have been used. The reset operation also
         * advances the next columns value.
         * @return
         */
        void advanceColumnValue() {
            if ( iterator.hasNext() ) {
                value = iterator.next();
            } else {
                isAllValuesUsed = true;
                this.iterator = this.values.iterator();
                this.value = iterator.next();
                int myIndex = columns.indexOf( this );
                if ( myIndex > 0 ) {
                    columns.get( myIndex - 1 ).advanceColumnValue();
                }
            }
        }

        /**
         * Have all values in the columns list been used
         * @return
         */
        boolean isAllValuesUsed() {
            return this.isAllValuesUsed;
        }

    }

    /**
     * Container for a columns values that are dynamically generated
     */
    static class ColumnDynamicValues extends ColumnValues {

        private final LimitedEntryDropDownManager.Context context;
        private final AsyncPackageDataModelOracle oracle;
        private boolean initialiseValueList = true;

        ColumnDynamicValues( final List<ColumnValues> columns,
                             final AsyncPackageDataModelOracle oracle,
                             final LimitedEntryDropDownManager.Context context,
                             final DTCellValue52 defaultValue ) {
            super( columns,
                   EMPTY_VALUE,
                   defaultValue );
            this.oracle = oracle;
            this.context = context;

            //Check if there is an enumeration
            final DropDownData dd = oracle.getEnums( context.getBasePattern().getFactType(),
                                                     ( (ConditionCol52) context.getBaseColumn() ).getFactField(),
                                                     new HashMap<String, String>() );
            if ( dd != null ) {
                this.values = convertValueList( getSplitValues( dd.getFixedList() ) );
                this.originalValues = this.values;
                this.initialiseValueList = false;
                this.isAllValuesUsed = false;
            }

            //Initialise value to the first in the list
            this.iterator = this.values.iterator();
            this.value = iterator.next();
        }

        private String[] getSplitValues( final String[] values ) {
            final String[] splitValues = new String[ values.length ];
            for ( int i = 0; i < values.length; i++ ) {
                final String v = values[ i ];
                final String[] splut = ConstraintValueEditorHelper.splitValue( v );
                splitValues[ i ] = splut[ 0 ];
            }
            return splitValues;
        }

        /**
         * Assert that the Value List is correct for data contained in the row
         * @param row
         * @returns true if the Value List has changed and the row should be
         * refreshed
         */
        boolean assertValueList( final List<DTCellValue52> row ) {
            if ( !this.expandColumn ) {
                return false;
            }
            final boolean refreshRow = this.initialiseValueList;
            if ( refreshRow ) {
                final Map<String, String> currentValueMap = new HashMap<String, String>();
                for ( int iCol = 0; iCol < this.columns.size(); iCol++ ) {
                    final ColumnValues cv = this.columns.get( iCol );
                    if ( cv instanceof ColumnDynamicValues ) {
                        final ColumnDynamicValues cdv = (ColumnDynamicValues) cv;
                        if ( cdv.context.getBasePattern().equals( this.context.getBasePattern() ) ) {
                            final ConditionCol52 cc = (ConditionCol52) cdv.context.getBaseColumn();
                            final DTCellValue52 value = row.get( iCol );
                            if ( value != null ) {
                                currentValueMap.put( cc.getFactField(),
                                                     value.getStringValue() );
                            }
                        }
                    }
                }
                this.initialiseValueList = false;
                final DropDownData dd = oracle.getEnums( context.getBasePattern().getFactType(),
                                                         ( (ConditionCol52) context.getBaseColumn() ).getFactField(),
                                                         currentValueMap );
                if ( dd != null ) {
                    this.values = convertValueList( getSplitValues( dd.getFixedList() ) );
                    this.originalValues = this.values;
                    this.isAllValuesUsed = false;
                } else {
                    this.values = new ArrayList<DTCellValue52>();
                    this.values.add( defaultValue );
                    this.originalValues = this.values;
                    this.isAllValuesUsed = true;
                }

                //Initialise value to the first in the list
                this.iterator = this.values.iterator();
                this.value = iterator.next();
            }
            return refreshRow;
        }

        /**
         * Advance to the next value for the column, resetting to the beginning
         * of the list if all values have been used. The reset operation also
         * advances the next columns value.
         * @return
         */
        void advanceColumnValue() {
            if ( iterator.hasNext() ) {
                value = iterator.next();
            } else {
                isAllValuesUsed = true;
                this.initialiseValueList = true;
                this.iterator = values.iterator();
                value = iterator.next();
                int myIndex = columns.indexOf( this );
                if ( myIndex > 0 ) {
                    columns.get( myIndex - 1 ).advanceColumnValue();
                }
            }
        }

    }

    /**
     * Container for Row Number column values
     */
    static class RowNumberColumnValues extends ColumnValues {

        RowNumberColumnValues( final List<ColumnValues> columns,
                               final List<DTCellValue52> values,
                               final DTCellValue52 defaultValue ) {
            super( columns,
                   values,
                   defaultValue );
        }

        @Override
        DTCellValue52 getCurrentValue() {
            //GUVNOR-1960: Always return a new instance
            return new DTCellValue52();
        }

    }

    /**
     * Container for Row Description column values
     */
    static class RowDescriptionColumnValues extends ColumnValues {

        RowDescriptionColumnValues( final List<ColumnValues> columns,
                                    final List<DTCellValue52> values,
                                    final DTCellValue52 defaultValue ) {
            super( columns,
                   values,
                   defaultValue );
        }

        @Override
        DTCellValue52 getCurrentValue() {
            //GUVNOR-1960: Always return a new instance
            return new DTCellValue52();
        }

    }

}
TOP

Related Classes of org.drools.workbench.screens.guided.dtable.client.wizard.pages.RowExpander$ColumnDynamicValues

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.