org.pentaho.aggdes.ui.form.controller.AggListController.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.aggdes.ui.form.controller.AggListController.java

Source

/*
* 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.
*
*
* Copyright 2006 - 2013 Pentaho Corporation.  All rights reserved.
*/

package org.pentaho.aggdes.ui.form.controller;

import java.awt.Color;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.DefaultIntervalXYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.pentaho.aggdes.algorithm.Algorithm;
import org.pentaho.aggdes.algorithm.util.ArgumentUtils;
import org.pentaho.aggdes.model.Aggregate;
import org.pentaho.aggdes.model.Parameter;
import org.pentaho.aggdes.output.OutputService;
import org.pentaho.aggdes.output.OutputValidationException;
import org.pentaho.aggdes.ui.Workspace;
import org.pentaho.aggdes.ui.ext.AlgorithmUiExtension;
import org.pentaho.aggdes.ui.form.model.AggModel;
import org.pentaho.aggdes.ui.form.model.AggregateSummaryModel;
import org.pentaho.aggdes.ui.form.model.ConnectionModel;
import org.pentaho.aggdes.ui.model.AggList;
import org.pentaho.aggdes.ui.model.AggListEvent;
import org.pentaho.aggdes.ui.model.AggListListener;
import org.pentaho.aggdes.ui.model.UIAggregate;
import org.pentaho.aggdes.ui.model.impl.UIAggregateImpl;
import org.pentaho.aggdes.ui.util.AggregateNamingService;
import org.pentaho.aggdes.ui.util.Messages;
import org.pentaho.ui.xul.XulException;
import org.pentaho.ui.xul.binding.Binding;
import org.pentaho.ui.xul.binding.BindingConvertor;
import org.pentaho.ui.xul.binding.BindingFactory;
import org.pentaho.ui.xul.binding.DefaultBinding;
import org.pentaho.ui.xul.components.XulButton;
import org.pentaho.ui.xul.components.XulImage;
import org.pentaho.ui.xul.components.XulTreeCell;
import org.pentaho.ui.xul.containers.XulTree;
import org.pentaho.ui.xul.containers.XulTreeRow;
import org.pentaho.ui.xul.impl.AbstractXulEventHandler;
import org.pentaho.ui.xul.stereotype.Controller;
import org.springframework.beans.factory.annotation.Autowired;

//FIXME: Use XUL data binding to remove all references to XulComponents

/**
 * AggListController manages the UI for the Aggregate Table and the Aggregate
 * Summary Screen, including the Cost / Benefit chart.
 */
@Controller
public class AggListController extends AbstractXulEventHandler {

    private final DecimalFormat format = new DecimalFormat("#0");

    private static final Log logger = LogFactory.getLog(AggListController.class);

    @Deprecated
    private XulTree aggLevelTable;

    @Deprecated
    private XulTree aggTable;

    private OutputService outputService;

    private AggModel aggModel;

    private AggregateNamingService aggNamingService;

    private AggregateSummaryModel aggregateSummaryModel;

    private Algorithm algorithm;

    private AlgorithmUiExtension algorithmUiExtension;

    private AggList aggList;

    private Workspace workspace;

    private ConnectionModel connectionModel;

    private BindingFactory bindingFactory;

    @Autowired
    public void setBindingFactory(BindingFactory bindingFactory) {
        this.bindingFactory = bindingFactory;
    }

    public AggList getAggList() {
        return aggList;
    }

    public void setAggList(AggList aggList) {

        this.aggList = aggList;
    }

    public void setAggModel(AggModel aggModel) {
        this.aggModel = aggModel;
    }

    public void setWorkspace(Workspace workspace) {
        this.workspace = workspace;
    }

    public void setOutputService(OutputService outputService) {
        this.outputService = outputService;
    }

    public void setAggregateNamingService(AggregateNamingService aggNamingService) {
        this.aggNamingService = aggNamingService;
    }

    public void setAggregateSummaryModel(AggregateSummaryModel aggregateSummaryModel) {
        this.aggregateSummaryModel = aggregateSummaryModel;
    }

    public void setAlgorithm(Algorithm algorithm) {
        this.algorithm = algorithm;
    }

    public void setAlgorithmUiExtension(AlgorithmUiExtension algorithmUiExtension) {
        this.algorithmUiExtension = algorithmUiExtension;
    }

    public void changeInAggregates() {

        // set dirty bit for workspace
        workspace.setWorkspaceUpToDate(false);

        // set the dirty bit for the schema publishing functionality

        if (getConnectionModel().getSchema() != null) {
            getConnectionModel().setSchemaUpToDate(false);
        }

        // configure summary model and chart values

        int totalAggregatesSelected = 0;
        double totalRows = 0;
        double totalSpace = 0;
        double totalLoadTime = 0;

        //Fire event
        AggListController.this.firePropertyChange("aggList", null, aggList);

        List<Aggregate> algoAggregates = new ArrayList<Aggregate>();
        for (UIAggregate aggregate : getAggList()) {
            if (aggregate.getEnabled()) {
                totalAggregatesSelected++;
                totalRows += aggregate.estimateRowCount();
                totalSpace += aggregate.estimateSpace();
                algoAggregates
                        .add(algorithm.createAggregate(connectionModel.getSchema(), aggregate.getAttributes()));
            }
        }

        double[] xs = new double[algoAggregates.size()];
        double[] startx = new double[algoAggregates.size()];
        double[] endx = new double[algoAggregates.size()];

        double[] ys = new double[algoAggregates.size()];
        double[] starty = new double[algoAggregates.size()];
        double[] endy = new double[algoAggregates.size()];

        XYSeries series1 = new XYSeries("CostBenefit");
        XYSeriesCollection dataset = new XYSeriesCollection();
        dataset.addSeries(series1);
        DefaultIntervalXYDataset datasetxy = new DefaultIntervalXYDataset();

        if (connectionModel.getSchema() != null) {

            Map<Parameter, Object> algorithmParams = ArgumentUtils.validateParameters(algorithm,
                    algorithmUiExtension.getAlgorithmParameters());
            List<Algorithm.CostBenefit> costBenefit = algorithm.computeAggregateCosts(connectionModel.getSchema(),
                    algorithmParams, algoAggregates);

            double totalbenefit = 0;
            double x = 0;
            int count = 0;
            for (Algorithm.CostBenefit cb : costBenefit) {
                Aggregate agg = algoAggregates.get(count);
                double estimateSpace = agg.estimateSpace();
                double hx = estimateSpace / 2;
                totalLoadTime += cb.getLoadTime();
                totalbenefit += cb.getSavedQueryRowCount();
                series1.add(x + hx, totalbenefit);

                xs[count] = x + hx;
                startx[count] = x;
                x += estimateSpace;
                endx[count] = x;

                ys[count] = totalbenefit;
                starty[count] = 0;
                endy[count] = 0;

                count++;
            }

            // update summary table

            aggregateSummaryModel.setSelectedAggregateCount(format.format(totalAggregatesSelected));
            aggregateSummaryModel.setSelectedAggregateRows(format.format(totalRows));
            aggregateSummaryModel.setSelectedAggregateSpace(format.format(totalSpace) + " bytes");
            aggregateSummaryModel.setSelectedAggregateLoadTime(format.format(totalLoadTime));
        } else {
            aggregateSummaryModel.setSelectedAggregateCount("");
            aggregateSummaryModel.setSelectedAggregateRows("");
            aggregateSummaryModel.setSelectedAggregateSpace("");
            aggregateSummaryModel.setSelectedAggregateLoadTime("");

        }
        // render cost benefit chart

        double[][] data = new double[][] { xs, startx, endx, ys, starty, endy };
        datasetxy.addSeries("", data);

        JFreeChart chart = ChartFactory.createXYBarChart("", // chart title
                "Cost", // x axis label
                false, "Benefit", // y axis label
                datasetxy, // data
                PlotOrientation.VERTICAL, // orientation
                false, // include legend
                false, // tooltips?
                false // URLs?
        );

        ((XYPlot) chart.getPlot()).getDomainAxis().setTickLabelsVisible(false);
        ((XYPlot) chart.getPlot()).getRangeAxis().setTickLabelsVisible(false);
        ((XYPlot) chart.getPlot()).setDataset(1, dataset);
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();

        chart.setBackgroundPaint(
                new Color(Integer.parseInt(Messages.getString("chart_background_color").toUpperCase(), 16)));

        renderer.setSeriesPaint(0,
                new Color(Integer.parseInt(Messages.getString("chart_line_color").toUpperCase(), 16)));

        ((XYPlot) chart.getPlot()).getRenderer(0).setSeriesPaint(0,
                new Color(Integer.parseInt(Messages.getString("chart_bar_color").toUpperCase(), 16)));

        ((XYPlot) chart.getPlot()).setRenderer(1, renderer);
        ((XYPlot) chart.getPlot()).setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
        XulImage image = (XulImage) document.getElementById("chart");
        image.setSrc(chart.createBufferedImage(309, 168));
    }

    public void onLoad() {
        bindingFactory.setDocument(document);
        bindingFactory.setBindingType(Binding.Type.BI_DIRECTIONAL);

        // bind summary model label details
        bindingFactory.createBinding(aggregateSummaryModel, "selectedAggregateCount", "num_aggs", "value");
        bindingFactory.createBinding(aggregateSummaryModel, "selectedAggregateRows", "num_rows", "value");
        bindingFactory.createBinding(aggregateSummaryModel, "selectedAggregateSpace", "disk_space", "value");

        //Bind check-all, un-check-all buttons to model
        BindingConvertor<AggList, Boolean> convertor = new BindingConvertor<AggList, Boolean>() {
            public Boolean sourceToTarget(AggList list) {
                return !(list.getSize() > 0);
            }

            //not used
            public AggList targetToSource(Boolean value) {
                return null;
            }
        };

        bindingFactory.setBindingType(Binding.Type.ONE_WAY);

        bindingFactory.createBinding(this, "aggList", "agg_checkall", "disabled", convertor);

        bindingFactory.createBinding(this, "aggList", "agg_uncheckall", "disabled", convertor);

        convertor = new BindingConvertor<AggList, Boolean>() {
            public Boolean sourceToTarget(AggList list) {
                return (list.getSize() > 0);
            }

            //not used
            public AggList targetToSource(Boolean value) {
                return null;
            }
        };

        bindingFactory.createBinding(this, "aggList", connectionModel, "schemaLocked", convertor);

        // not displayed at this time, no unit of measure
        // bind(aggregateSummaryModel, "selectedAggregateLoadTime", "load_time", "value");

        aggLevelTable = (XulTree) document.getElementById("dimtable");
        aggTable = (XulTree) document.getElementById("definedAggTable");

        AggListListener aggListListener = new AggListListener() {

            public void listChanged(AggListEvent e) {
                final String ESTIMATE_UNKNOWN = "unknown";

                AggList list = (AggList) e.getSource();
                switch (e.getType()) {
                case AGGS_ADDED:
                    try {
                        for (int i = e.getIndex(); i < list.getSize(); i++) {
                            UIAggregate newAgg = list.getAgg(i);

                            XulTreeRow newRow = (XulTreeRow) document.createElement("treerow");

                            XulTreeCell newCell = (XulTreeCell) document.createElement("treecell");
                            newCell.setValue(newAgg.getEnabled());
                            newRow.addCell(newCell);

                            newCell = (XulTreeCell) document.createElement("treecell");
                            newCell.setLabel(newAgg.isAlgoAgg() ? Messages.getString("agg_type_advisor")
                                    : Messages.getString("agg_type_custom"));
                            newRow.addCell(newCell);

                            newCell = (XulTreeCell) document.createElement("treecell");
                            newCell.setLabel(newAgg.getName());
                            newRow.addCell(newCell);

                            newCell = (XulTreeCell) document.createElement("treecell");
                            double rowCount = newAgg.estimateRowCount();
                            if (rowCount == 0) {
                                newCell.setLabel(ESTIMATE_UNKNOWN);
                            } else {
                                newCell.setLabel(format.format(rowCount));
                            }
                            newRow.addCell(newCell);

                            newCell = (XulTreeCell) document.createElement("treecell");
                            double space = newAgg.estimateSpace();
                            if (space == 0) {
                                newCell.setLabel(ESTIMATE_UNKNOWN);
                            } else {
                                newCell.setLabel(format.format(space));
                            }
                            newRow.addCell(newCell);

                            aggTable.addTreeRow(newRow);
                        }
                        changeInAggregates();

                    } catch (XulException xe) {
                        logger.error("Error adding new row to Agg table", xe);
                    }
                    break;

                case AGG_ADDED:
                    try {
                        UIAggregate newAgg = list.getAgg(e.getIndex());

                        XulTreeRow newRow = (XulTreeRow) document.createElement("treerow");

                        XulTreeCell newCell = (XulTreeCell) document.createElement("treecell");
                        newCell.setValue(newAgg.getEnabled());
                        newRow.addCell(newCell);

                        newCell = (XulTreeCell) document.createElement("treecell");
                        newCell.setLabel(newAgg.isAlgoAgg() ? Messages.getString("agg_type_advisor")
                                : Messages.getString("agg_type_custom"));
                        newRow.addCell(newCell);

                        newCell = (XulTreeCell) document.createElement("treecell");
                        newCell.setLabel(newAgg.getName());
                        newRow.addCell(newCell);

                        newCell = (XulTreeCell) document.createElement("treecell");
                        double rowCount = newAgg.estimateRowCount();
                        if (rowCount == 0) {
                            newCell.setLabel(ESTIMATE_UNKNOWN);
                        } else {
                            newCell.setLabel(format.format(rowCount));
                        }
                        newRow.addCell(newCell);

                        newCell = (XulTreeCell) document.createElement("treecell");
                        double space = newAgg.estimateSpace();
                        if (space == 0) {
                            newCell.setLabel(ESTIMATE_UNKNOWN);
                        } else {
                            newCell.setLabel(format.format(space));
                        }
                        newRow.addCell(newCell);

                        aggTable.addTreeRow(newRow);

                        changeInAggregates();

                    } catch (XulException xe) {
                        logger.error("Error adding new row to Agg table", xe);
                    }
                    break;
                case AGGS_CLEARED:
                    aggTable.getRootChildren().removeAll();
                    aggModel.setThinAgg(null);
                    changeInAggregates();
                    break;
                case AGG_REMOVED:
                    aggTable.removeTreeRows(new int[] { e.getIndex() });
                    aggTable.clearSelection();
                    int[] tableSelection = aggTable.getSelectedRows();
                    if (e.getIndex() < list.getSize()) {
                        //we're single selection
                        list.setSelectedIndex(e.getIndex());
                    }
                    //There's aready a selection listener on the table firing the same.
                    UIAggregate tempAgg = list.getSelectedValue();

                    aggModel.setThinAgg(list.getSelectedValue());

                    changeInAggregates();

                    break;
                case SELECTION_CHANGED:

                    if (aggTable.getSelectedRows().length > 0) {
                        logger.info("Event index: " + e.getIndex() + " aggTable.selectedRow0: "
                                + aggTable.getSelectedRows()[0]);
                    }
                    int[] rows = aggTable.getSelectedRows();
                    if (aggTable.getSelectedRows().length > 0 && e.getIndex() == aggTable.getSelectedRows()[0]) {
                        aggModel.setThinAgg(list.getSelectedValue());
                    }
                    //check to see if it's already selected, ignore if so
                    int[] curSelection = aggTable.getSelectedRows();
                    //            if(curSelection.length > 0 && e.getIndex() == curSelection[0]){
                    //              logger.debug("already selected row");
                    //              break;
                    //            }
                    logger.debug("selecting new row");
                    aggTable.setSelectedRows(new int[] { e.getIndex() });
                    aggModel.setThinAgg(list.getSelectedValue());
                    break;
                case AGG_CHANGED:
                    logger.debug("agg list controller responding to AGG_CHANGED event from aggList");
                    int idx = e.getIndex();
                    UIAggregate agg = list.getAgg(idx);
                    aggTable.getRootChildren().getItem(idx).getRow().getCell(0).setValue(agg.getEnabled());
                    aggTable.getRootChildren().getItem(idx).getRow().getCell(1)
                            .setLabel(agg.isAlgoAgg() ? Messages.getString("agg_type_advisor")
                                    : Messages.getString("agg_type_custom"));
                    aggTable.getRootChildren().getItem(idx).getRow().getCell(2).setLabel(agg.getName());

                    double rowCount = agg.estimateRowCount();
                    if (rowCount == 0) {
                        aggTable.getRootChildren().getItem(idx).getRow().getCell(3).setLabel(ESTIMATE_UNKNOWN);
                    } else {
                        aggTable.getRootChildren().getItem(idx).getRow().getCell(3)
                                .setLabel(format.format(rowCount));
                    }

                    double space = agg.estimateSpace();
                    if (space == 0) {
                        aggTable.getRootChildren().getItem(idx).getRow().getCell(4).setLabel(ESTIMATE_UNKNOWN);
                    } else {
                        aggTable.getRootChildren().getItem(idx).getRow().getCell(4).setLabel(format.format(space));
                    }

                    // wrap the update in an invokeLater to avoid null pointer exceptions occurring deep in Swing code,
                    // even though this method will normally be called within the event thread.
                    document.invokeLater(new Runnable() {
                        public void run() {
                            aggTable.update();
                        }
                    });
                    changeInAggregates();

                    break;
                }

            }

        };
        getAggList().addAggListListener(aggListListener);

        // what happens?
        changeInAggregates();
    }

    public void addAgg() {
        try {
            UIAggregate newAgg = new UIAggregateImpl();
            aggNamingService.nameAggregate(newAgg, getAggList(), connectionModel.getSchema());

            newAgg.setOutput(outputService.generateDefaultOutput(newAgg));

            getAggList().addAgg(newAgg);
            getAggList().setSelectedIndex(getAggList().getSize() - 1);
        } catch (OutputValidationException e) {
            System.err.println("Failed to create output, skipping UIAggregate");
            e.printStackTrace();
        }
    }

    public void showAgg(int idx) {
        aggLevelTable.clearSelection();
        Aggregate agg = getAggList().getAgg(idx);
        if (agg == null) {
            logger.info(String.format("List and Table out of sync, %s does not exist", idx));
        } else {
            getAggList().setSelectedIndex(idx);
        }
    }

    public void saveAggChange(int idx) {
        UIAggregate agg = getAggList().getAgg(idx);
        XulTree aggTable = (XulTree) document.getElementById("definedAggTable");
        XulTreeRow row = aggTable.getRootChildren().getItem(idx).getRow();
        agg.setEnabled((Boolean) row.getCell(0).getValue());

        // get row count estimate
        Aggregate algoAggregate = algorithm.createAggregate(connectionModel.getSchema(), agg.getAttributes());
        agg.setEstimateRowCount(algoAggregate.estimateRowCount());
        agg.setEstimateSpace(algoAggregate.estimateSpace());
        getAggList().aggChanged(agg);
        System.out.println("Saving agg, enabled? " + row.getCell(0).getValue());

    }

    public void displayNewOrExistingAgg() {
        if (getAggList().getSize() == 0) {
            addAgg();
        } else {
            if (getAggList().getSelectedIndex() == -1) {
                showAgg(0);
            } else {
                showAgg(getAggList().getSelectedIndex());
            }
        }
    }

    public void removeAgg() {
        if (aggTable == null) {
            return;
        }
        int[] selectedIndexes = aggTable.getSelectedRows();
        for (int pos : selectedIndexes) {
            //the user has chosen to delete this agg, so do not prompt to save any changes
            //just reset the form.
            aggModel.reset();

            getAggList().removeAgg(pos);
        }

    }

    public void moveAggUp() {
        getAggList().moveAggUp(getAggList().getSelectedValue());
    }

    public void moveAggDown() {
        getAggList().moveAggDown(getAggList().getSelectedValue());
    }

    public void checkAll() {
        getAggList().checkAll();
    }

    public void uncheckAll() {
        getAggList().uncheckAll();
    }

    public ConnectionModel getConnectionModel() {

        return connectionModel;
    }

    public void setConnectionModel(ConnectionModel connectionModel) {

        this.connectionModel = connectionModel;
    }
}