Package org.drools.guvnor.client.widgets.decoratedgrid.data

Source Code of org.drools.guvnor.client.widgets.decoratedgrid.data.DynamicData

/*
* 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.guvnor.client.widgets.decoratedgrid.data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.drools.guvnor.client.widgets.decoratedgrid.CellValue;
import org.drools.guvnor.client.widgets.decoratedgrid.CellValue.CellState;
import org.drools.guvnor.client.widgets.decoratedgrid.CellValue.GroupedCellValue;
import org.drools.guvnor.client.widgets.decoratedgrid.SortConfiguration;

import com.google.gwt.user.client.Command;

/**
* A simple container for rows of data.
*/
public class DynamicData
    implements
    Iterable<DynamicDataRow> {

    private boolean              isMerged         = false;

    private List<Boolean>        visibleColumns   = new ArrayList<Boolean>();

    private static final long    serialVersionUID = 5061393855340039472L;

    private Command              onRowChangeCommand;

    private List<DynamicDataRow> data             = new ArrayList<DynamicDataRow>();

    /**
     * Add column to data
     *
     * @param index
     * @param columnData
     */
    public void addColumn(int index,
                          List<CellValue< ? extends Comparable< ? >>> columnData,
                          boolean isVisible) {
        for ( int iRow = 0; iRow < columnData.size(); iRow++ ) {
            CellValue< ? > cv = columnData.get( iRow );
            data.get( iRow ).add( index,
                                  cv );
        }
        visibleColumns.add( index,
                            isVisible );

        assertModelMerging();
    }

    /**
     * Add an empty row of data to the end of the table
     *
     * @return DynamicDataRow The newly created row
     */
    public DynamicDataRow addRow() {
        DynamicDataRow row = new DynamicDataRow();
        data.add( row );

        //Execute command if set
        if ( onRowChangeCommand != null ) {
            onRowChangeCommand.execute();
        }

        assertModelMerging();
        return row;
    }

    /**
     * Add a row of data at the specified index
     *
     * @param index
     * @param rowData
     * @return DynamicDataRow The newly created row
     */
    public DynamicDataRow addRow(int index,
                                 List<CellValue< ? extends Comparable< ? >>> rowData) {
        DynamicDataRow row = new DynamicDataRow();
        for ( CellValue< ? extends Comparable< ? >> cell : rowData ) {
            row.add( cell );
        }
        data.add( index,
                  row );

        //Execute command if set
        if ( onRowChangeCommand != null ) {
            onRowChangeCommand.execute();
        }

        assertModelMerging();
        return row;
    }

    /**
     * Add a row of data at the end of the table
     *
     * @param rowData
     * @return DynamicDataRow The newly created row
     */
    public DynamicDataRow addRow(List<CellValue< ? extends Comparable< ? >>> rowData) {
        return addRow( data.size(),
                       rowData );
    }

    /**
     * Apply grouping by collapsing applicable rows
     *
     * @param startCell
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public void applyModelGrouping(CellValue< ? > startCell) {

        int startRowIndex = startCell.getCoordinate().getRow();
        int endRowIndex = findMergedCellExtent( startCell.getCoordinate() ).getRow();
        int colIndex = startCell.getCoordinate().getCol();

        //Delete grouped rows replacing with a single "grouped" row
        GroupedCellValue groupedCell;
        DynamicDataRow row = data.get( startRowIndex );
        GroupedDynamicDataRow groupedRow = new GroupedDynamicDataRow();
        for ( int iCol = 0; iCol < row.size(); iCol++ ) {
            groupedCell = row.get( iCol ).convertToGroupedCell();
            if ( iCol == colIndex ) {
                groupedCell.addState( CellState.GROUPED );
            } else {
                groupedCell.removeState( CellState.GROUPED );
            }
            groupedRow.add( groupedCell );
        }

        //Add individual cells to "grouped" row
        for ( int iRow = startRowIndex; iRow <= endRowIndex; iRow++ ) {
            DynamicDataRow childRow = data.get( startRowIndex );
            groupedRow.addChildRow( childRow );
            data.remove( childRow );
        }
        data.remove( row );
        data.add( startRowIndex,
                  groupedRow );

        //Execute command if set
        if ( onRowChangeCommand != null ) {
            onRowChangeCommand.execute();
        }

        assertModelMerging();
    }

    public void clear() {
        data.clear();
    }

    /**
     * Delete column data
     *
     * @param index
     */
    public void deleteColumn(int index) {
        for ( int iRow = 0; iRow < data.size(); iRow++ ) {
            DynamicDataRow row = data.get( iRow );
            row.remove( index );
        }
        visibleColumns.remove( index );
        assertModelMerging();
    }

    public DynamicDataRow deleteRow(int index) {
        DynamicDataRow row = data.remove( index );

        //Execute command if set
        if ( onRowChangeCommand != null ) {
            onRowChangeCommand.execute();
        }

        assertModelMerging();
        return row;
    }

    /**
     * Get the CellValue at the given coordinate
     *
     * @param c
     * @return
     */
    public CellValue< ? extends Comparable< ? >> get(Coordinate c) {
        return data.get( c.getRow() ).get( c.getCol() );
    }

    public DynamicDataRow get(int index) {
        return data.get( index );
    }

    /**
     * Return grid's data. Grouping in the data will be expanded and can
     * therefore can be used prior to populate the underlying data structures
     * prior to persisting.
     *
     * @return data
     */
    public DynamicData getFlattenedData() {
        DynamicData dataClone = new DynamicData();
        for ( int iRow = 0; iRow < data.size(); iRow++ ) {
            DynamicDataRow row = data.get( iRow );
            if ( row instanceof GroupedDynamicDataRow ) {
                List<DynamicDataRow> expandedRow = expandGroupedRow( row,
                                                                      true );
                dataClone.data.addAll( expandedRow );
            } else {
                dataClone.data.add( row );
            }
        }
        return dataClone;
    }

    public int indexOf(DynamicDataRow row) {
        return data.indexOf( row );
    }

    /**
     * Return the state of merging
     *
     * @return
     */
    public boolean isMerged() {
        return isMerged;
    }

    public Iterator<DynamicDataRow> iterator() {
        return data.iterator();
    }

    /**
     * Remove grouping by expanding applicable rows
     *
     * @param startCell
     * @return
     */
    @SuppressWarnings("rawtypes")
    public List<DynamicDataRow> removeModelGrouping(CellValue< ? > startCell) {

        int startRowIndex = startCell.getCoordinate().getRow();

        startCell.removeState( CellState.GROUPED );

        //Check if rows need to be recursively expanded
        boolean bRecursive = true;
        DynamicDataRow row = data.get( startRowIndex );
        for ( int iCol = 0; iCol < row.size(); iCol++ ) {
            CellValue< ? > cv = row.get( iCol );
            if ( cv instanceof GroupedCellValue ) {
                bRecursive = !(bRecursive ^ ((GroupedCellValue) cv).hasMultipleValues());
            }
        }

        //Delete "grouped" row and replace with individual rows
        List<DynamicDataRow> expandedRow = expandGroupedRow( row,
                                                             bRecursive );
        deleteRow( startRowIndex );
        data.addAll( startRowIndex,
                     expandedRow );

        //Execute command if set
        if ( onRowChangeCommand != null ) {
            onRowChangeCommand.execute();
        }

        assertModelMerging();

        //If the row is replaced with another grouped row ensure the row can be expanded
        row = data.get( startRowIndex );
        boolean hasCellToExpand = false;
        for ( CellValue< ? > cell : row ) {
            if ( cell instanceof GroupedCellValue ) {
                if ( cell.isGrouped() && cell.getRowSpan() > 0 ) {
                    hasCellToExpand = true;
                    break;
                }
            }
        }
        if ( !hasCellToExpand ) {
            for ( CellValue< ? > cell : row ) {
                if ( cell instanceof GroupedCellValue && cell.getRowSpan() == 1 ) {
                    cell.addState( CellState.GROUPED );
                }
            }
        }
        return expandedRow;
    }

    /**
     * Set the value at the specified coordinate
     *
     * @param c
     * @param value
     */
    public void set(Coordinate c,
                    Object value) {
        if ( c == null ) {
            throw new IllegalArgumentException( "c cannot be null" );
        }
        data.get( c.getRow() ).get( c.getCol() ).setValue( value );
        assertModelMerging();
    }

    /**
     * Set whether a columns is Visible
     *
     * @param index
     *            index of column
     * @param isVisible
     *            True if the column is visible
     */
    public void setColumnVisibility(int index,
                                    boolean isVisible) {
        this.visibleColumns.set( index,
                                 isVisible );
        assertModelIndexes();
    }

    /**
     * Set whether the grid's data is merged or not. Clearing merging within the
     * data also clears grouping
     *
     * @param isMerged
     */
    public void setMerged(boolean isMerged) {
        this.isMerged = isMerged;
        if ( isMerged ) {
            assertModelMerging();
        } else {
            removeModelGrouping();
            removeModelMerging();
        }
    }

    /**
     * Configure a command that can be executed whenever a row is added\deleted.
     * This allows consumers of DynamicData to ensure column values are within
     * keeping of their requirements. E.G. A Column containing a Row Number can
     * be updated etc
     *
     * @param cmd
     */
    public void setOnRowChangeCommand(Command cmd) {
        this.onRowChangeCommand = cmd;
    }

    public int size() {
        return data.size();
    }

    public void sort(final List<SortConfiguration> sortConfig) {

        if ( sortConfig.size() == 0 ) {

            //No sort configuration, restore original creation sequence
            Collections.sort( data,
                              new Comparator<DynamicDataRow>() {

                                  public int compare(DynamicDataRow leftRow,
                                                     DynamicDataRow rightRow) {
                                      int comparison = 0;
                                      long li = leftRow.getCreationIndex();
                                      long ri = rightRow.getCreationIndex();
                                      if ( li < ri ) {
                                          comparison = -1;
                                      } else if ( li > ri ) {
                                          comparison = 1;
                                      }
                                      return comparison;
                                  }
                              } );

        } else {

            //Sort Configuration needs to be sorted itself by sortOrder first
            Collections.sort( sortConfig,
                              new Comparator<SortConfiguration>() {

                                  public int compare(SortConfiguration o1,
                                                     SortConfiguration o2) {
                                      Integer si1 = o1.getSortIndex();
                                      Integer si2 = o2.getSortIndex();
                                      return si1.compareTo( si2 );
                                  }

                              } );

            //Sort data
            Collections.sort( data,
                              new Comparator<DynamicDataRow>() {

                                  @SuppressWarnings({"rawtypes", "unchecked"})
                                  public int compare(DynamicDataRow leftRow,
                                                     DynamicDataRow rightRow) {
                                      int comparison = 0;
                                      for ( int index = 0; index < sortConfig.size(); index++ ) {
                                          SortConfiguration sc = sortConfig.get( index );
                                          Comparable leftColumnValue = leftRow.get( sc.getColumnIndex() );
                                          Comparable rightColumnValue = rightRow.get( sc.getColumnIndex() );
                                          comparison =
                                                  (leftColumnValue == rightColumnValue) ? 0
                                                      : (leftColumnValue == null) ? -1
                                                          : (rightColumnValue == null) ? 1
                                                              : leftColumnValue.compareTo( rightColumnValue );
                                          if ( comparison != 0 ) {
                                              switch ( sc.getSortDirection() ) {
                                                  case ASCENDING :
                              break;
                          case DESCENDING :
                              comparison = -comparison;
                              break;
                          default :
                              throw new IllegalStateException(
                                                               "Sorting can only be enabled for ASCENDING or"
                                                                       + " DESCENDING, not sortDirection ("
                                                                       + sc.getSortDirection()
                                                                       + ") ." );
                      }
                      return comparison;
                  }
              }
              return comparison;
          }
                              } );
        }

        //Execute command if set
        if ( onRowChangeCommand != null ) {
            onRowChangeCommand.execute();
        }

        assertModelMerging();

    }

    // Here lays a can of worms! Each cell in the Decision Table has three
    // coordinates: (1) The physical coordinate, (2) The coordinate relating to
    // the HTML table element and (3) The coordinate mapping a HTML table
    // element back to the physical coordinate. For example a cell could have
    // the (1) physical coordinate (0,0) which equates to (2) HTML element (0,1)
    // in which case the cell at physical coordinate (0,1) would have a (3)
    // mapping back to (0,0).
    private void assertModelIndexes() {

        if ( data.size() == 0 ) {
            return;
        }

        for ( int iRow = 0; iRow < data.size(); iRow++ ) {
            DynamicDataRow row = data.get( iRow );

            int colCount = 0;
            for ( int iCol = 0; iCol < row.size(); iCol++ ) {

                int newRow = iRow;
                int newCol = colCount;
                CellValue< ? extends Comparable< ? >> indexCell = row.get( iCol );

                // Don't index hidden columns; indexing is used to
                // map between HTML elements and the data behind
                if ( visibleColumns.get( iCol ) ) {

                    if ( indexCell.getRowSpan() != 0 ) {
                        newRow = iRow;
                        newCol = colCount++;

                        CellValue< ? extends Comparable< ? >> cell = data.get( newRow ).get( newCol );
                        cell.setPhysicalCoordinate( new Coordinate( iRow,
                                                                    iCol ) );

                    } else {
                        DynamicDataRow priorRow = data.get( iRow - 1 );
                        CellValue< ? extends Comparable< ? >> priorCell = priorRow.get( iCol );
                        Coordinate priorHtmlCoordinate = priorCell.getHtmlCoordinate();
                        newRow = priorHtmlCoordinate.getRow();
                        newCol = priorHtmlCoordinate.getCol();
                    }
                }
                indexCell.setCoordinate( new Coordinate( iRow,
                                                         iCol ) );
                indexCell.setHtmlCoordinate( new Coordinate( newRow,
                                                             newCol ) );
            }
        }
    }

    //Ensure merging and indexing is reflected in the entire model
    @SuppressWarnings({"unchecked", "rawtypes"})
    private void assertModelMerging() {

        if ( data.size() == 0 ) {
            return;
        }

        //Remove merging first as it initialises all coordinates
        removeModelMerging();

        final int COLUMNS = data.get( 0 ).size();

        //Only apply merging if merged
        if ( isMerged ) {

            int minRowIndex = 0;
            int maxRowIndex = data.size();

            //Add an empty row to the end of the data to simplify detection of merged cells that run to the end of the table
            DynamicDataRow blankRow = new DynamicDataRow();
            for ( int iCol = 0; iCol < COLUMNS; iCol++ ) {
                CellValue cv = new CellValue( null,
                                              maxRowIndex,
                                              iCol );
                blankRow.add( cv );
            }
            data.add( blankRow );
            maxRowIndex++;

            //Look in columns for cells with identical values
            for ( int iCol = 0; iCol < COLUMNS; iCol++ ) {
                CellValue< ? > cell1 = data.get( minRowIndex ).get( iCol );
                CellValue< ? > cell2 = null;
                for ( int iRow = minRowIndex + 1; iRow < maxRowIndex; iRow++ ) {
                    cell1.setRowSpan( 1 );
                    cell2 = data.get( iRow ).get( iCol );

                    //Merge if both cells contain the same value and neither is grouped
                    boolean bSplit = true;
                    if ( !cell1.isEmpty() && !cell2.isEmpty() ) {
                        if ( cell1.getValue().equals( cell2.getValue() ) ) {
                            bSplit = false;
                            if ( cell1 instanceof GroupedCellValue ) {
                                GroupedCellValue gcv = (GroupedCellValue) cell1;
                                if ( gcv.hasMultipleValues() ) {
                                    bSplit = true;
                                }
                            }
                            if ( cell2 instanceof GroupedCellValue ) {
                                GroupedCellValue gcv = (GroupedCellValue) cell2;
                                if ( gcv.hasMultipleValues() ) {
                                    bSplit = true;
                                }
                            }
                        }
                    } else if ( cell1.isOtherwise() && cell2.isOtherwise() ) {
                        bSplit = false;
                        if ( cell1 instanceof GroupedCellValue ) {
                            GroupedCellValue gcv = (GroupedCellValue) cell1;
                            if ( gcv.hasMultipleValues() ) {
                                bSplit = true;
                            }
                        }
                        if ( cell2 instanceof GroupedCellValue ) {
                            GroupedCellValue gcv = (GroupedCellValue) cell2;
                            if ( gcv.hasMultipleValues() ) {
                                bSplit = true;
                            }
                        }
                    }

                    if ( bSplit ) {
                        mergeCells( cell1,
                                    cell2 );
                        cell1 = cell2;
                    }

                }
            }

            //Remove dummy blank row
            data.remove( blankRow );

        }

        // Set indexes after merging has been corrected
        assertModelIndexes();

    }

    //Expand a grouped row and return a list of expanded rows
    private List<DynamicDataRow> expandGroupedRow(DynamicDataRow row,
                                                   boolean bRecursive) {

        List<DynamicDataRow> ungroupedRows = new ArrayList<DynamicDataRow>();

        if ( row instanceof GroupedDynamicDataRow ) {

            GroupedDynamicDataRow groupedRow = (GroupedDynamicDataRow) row;
            for ( int iChildRow = 0; iChildRow < groupedRow.getChildRows().size(); iChildRow++ ) {
                DynamicDataRow childRow = groupedRow.getChildRows().get( iChildRow );

                if ( bRecursive ) {
                    if ( childRow instanceof GroupedDynamicDataRow ) {
                        List<DynamicDataRow> expandedRow = expandGroupedRow( childRow,
                                                                              bRecursive );
                        ungroupedRows.addAll( expandedRow );
                    } else {
                        ungroupCells( childRow );
                        ungroupedRows.add( childRow );
                    }
                } else {
                    ungroupedRows.add( childRow );
                }
            }
        } else {
            ungroupCells( row );
            ungroupedRows.add( row );
        }

        return ungroupedRows;
    }

    //Find the bottom coordinate of a merged cell
    private Coordinate findMergedCellExtent(Coordinate c) {
        if ( c.getRow() == data.size() - 1 ) {
            return c;
        }
        Coordinate nc = new Coordinate( c.getRow() + 1,
                                        c.getCol() );
        CellValue< ? > newCell = get( nc );
        while ( newCell.getRowSpan() == 0 && nc.getRow() < data.size() - 1 ) {
            nc = new Coordinate( nc.getRow() + 1,
                                     nc.getCol() );
            newCell = get( nc );
        }
        if ( newCell.getRowSpan() != 0 ) {
            nc = new Coordinate( nc.getRow() - 1,
                                     nc.getCol() );
            newCell = get( nc );
        }
        return nc;
    }

    //Merge between the two provided cells
    private void mergeCells(CellValue< ? > cell1,
                            CellValue< ? > cell2) {
        int iStartRowIndex = cell1.getCoordinate().getRow();
        int iEndRowIndex = cell2.getCoordinate().getRow();
        int iColIndex = cell1.getCoordinate().getCol();

        //Any rows that are grouped need row span of zero
        for ( int iRow = iStartRowIndex; iRow < iEndRowIndex; iRow++ ) {
            DynamicDataRow row = data.get( iRow );
            row.get( iColIndex ).setRowSpan( 0 );
        }
        cell1.setRowSpan( iEndRowIndex - iStartRowIndex );

    }

    //Initialise cell parameters when ungrouped
    private void ungroupCells(DynamicDataRow row) {
        for ( int iCol = 0; iCol < row.size(); iCol++ ) {
            CellValue< ? > cell = row.get( iCol );
            cell.removeState( CellState.GROUPED );
        }
    }

    /**
     * Remove all grouping throughout the model
     */
    void removeModelGrouping() {

        for ( int iRow = 0; iRow < data.size(); iRow++ ) {
            DynamicDataRow row = data.get( iRow );
            if ( row instanceof GroupedDynamicDataRow ) {
                List<DynamicDataRow> expandedRow = expandGroupedRow( row,
                                                                     true );
                deleteRow( iRow );
                data.addAll( iRow,
                             expandedRow );
                iRow = iRow + expandedRow.size() - 1;
            }
        }

    }

    /**
     * Remove merging from model
     */
    void removeModelMerging() {

        for ( int iRow = 0; iRow < data.size(); iRow++ ) {
            DynamicDataRow row = data.get( iRow );

            for ( int iCol = 0; iCol < row.size(); iCol++ ) {
                CellValue< ? > cell = row.get( iCol );
                Coordinate c = new Coordinate( iRow,
                                               iCol );
                cell.setCoordinate( c );
                cell.setHtmlCoordinate( c );
                cell.setPhysicalCoordinate( c );
                cell.setRowSpan( 1 );
            }
        }

        // Set indexes after merging has been corrected
        assertModelIndexes();
    }

}
TOP

Related Classes of org.drools.guvnor.client.widgets.decoratedgrid.data.DynamicData

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.