ro.nextreports.server.pivot.DefaultPivotModel.java Source code

Java tutorial

Introduction

Here is the source code for ro.nextreports.server.pivot.DefaultPivotModel.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 ro.nextreports.server.pivot;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.MultiKeyMap;

import ro.nextreports.server.pivot.tree.Node;
import ro.nextreports.server.pivot.tree.Tree;
import ro.nextreports.server.pivot.tree.TreeHelper;

/**
 * @author Decebal Suiu
 */
public class DefaultPivotModel implements PivotModel {

    private static final long serialVersionUID = 1L;

    private PivotDataSource dataSource;
    private List<PivotField> fields;
    private Tree columnsHeaderTree;
    private Tree rowsHeaderTree;
    private List<MultiKeyMap> calculatedData; // or use a MultiValueMap from apache commons

    private boolean showGrandTotalForColumn;
    private boolean showGrandTotalForRow;

    public DefaultPivotModel(PivotDataSource dataSource) {
        this.dataSource = dataSource;

        // init fields
        int count = dataSource.getFieldCount();
        fields = new ArrayList<PivotField>(count);
        for (int i = 0; i < count; i++) {
            PivotField field = new PivotField(dataSource.getFieldName(i), i);
            field.setTitle(field.getName());
            field.setArea(PivotField.Area.UNUSED);
            field.setType(dataSource.getFieldType(i));
            fields.add(field);
        }
    }

    public List<PivotField> getFields() {
        return fields;
    }

    public PivotField getField(String name) {
        for (PivotField field : fields) {
            if (field.getName().equals(name)) {
                return field;
            }
        }

        return null;
    }

    public PivotField getField(int index) {
        for (PivotField field : fields) {
            if (field.getIndex() == index) {
                return field;
            }
        }

        return null;
    }

    public List<PivotField> getFields(PivotField.Area area) {
        List<PivotField> areaFields = new ArrayList<PivotField>();
        List<PivotField> fields = getFields();
        for (PivotField field : fields) {
            if (field.getArea().equals(area)) {
                areaFields.add(field);
            }
        }
        Collections.sort(areaFields);

        return areaFields;
    }

    public PivotDataSource getDataSource() {
        return dataSource;
    }

    public void calculate() {
        long start = System.currentTimeMillis();
        rowsHeaderTree = null;
        columnsHeaderTree = null;
        getRowsHeaderTree();
        long t1 = System.currentTimeMillis();
        System.out.println("created rowsHeaderTree in " + (t1 - start));
        getColumnsHeaderTree();
        long t2 = System.currentTimeMillis();
        System.out.println("created columnsHeaderTree in " + (t2 - t1));

        t1 = System.currentTimeMillis();
        List<PivotField> dataFields = getFields(PivotField.Area.DATA);
        calculatedData = new ArrayList<MultiKeyMap>();
        for (PivotField field : dataFields) {
            field.getAggregator().init();
            calculatedData.add(getData(field));
        }
        t2 = System.currentTimeMillis();
        System.out.println("filled calculatedData in " + (t2 - t1));
        long stop = System.currentTimeMillis();
        System.out.println("calculated in " + (stop - start));
        System.out.println("calculatedData = " + calculatedData);
        // getValues(field, filter)
    }

    /*
     * TODO: trebuie imbunatatita metoda asta. Am facut un test pe un tabel
     * cu 4500 inregistrari si 7 coloane (nextreports downloads). Am observat ca
     * la 86 chei pe row si 212 chei pe column am 18.232 (86 x 212) combinatii.
     * Daca in getValues se sta 3,25 ms (cum am obtinut) rezulta un total de 
     * 5576 ms. Cred ca ar trebuii sa parcurg o singura data inregistrarile din baza.
     */
    private MultiKeyMap getData(PivotField dataField) {
        MultiKeyMap data = new MultiKeyMap();
        List<List<Object>> rowKeys = getRowKeys();
        System.out.println("rowKeys.size() = " + rowKeys.size());
        List<List<Object>> columnKeys = getColumnKeys();
        System.out.println("columnKeys.size() = " + columnKeys.size());

        List<PivotField> rowFields = getFields(PivotField.Area.ROW);
        List<PivotField> columnFields = getFields(PivotField.Area.COLUMN);
        for (List<Object> rowKey : rowKeys) {
            for (List<Object> columnKey : columnKeys) {
                Map<Integer, Object> rowFilter = getFilter(rowFields, rowKey);
                Map<Integer, Object> columnFilter = getFilter(columnFields, columnKey);
                Map<Integer, Object> filter = new HashMap<Integer, Object>(rowFilter);
                filter.putAll(columnFilter);
                List<Object> values = getValues(dataField, filter);
                if (!CollectionUtils.isEmpty(values)) {
                    /*
                    System.out.println("filter = " + filter);
                    System.out.println("values = " + values);
                    System.out.println(values.size());
                    */
                    Object summary = PivotUtils.getSummary(dataField, values);
                    //               System.out.println("summary = " + summary);
                    data.put(rowKey, columnKey, summary);
                }
            }
        }

        return data;
    }

    /*
    @SuppressWarnings("unchecked")
    private MultiKeyMap getData2(PivotField dataField) {
       MultiKeyMap data = new MultiKeyMap();
       List<List<Object>> rowKeys = getRowKeys();
       System.out.println("rowKeys.size() = " + rowKeys.size());
       List<List<Object>> columnKeys = getColumnKeys();
       System.out.println("columnKeys.size() = " + columnKeys.size());
           
       MultiKeyMap filtersMap = new MultiKeyMap();
       List<PivotField> rowFields = getFields(PivotField.Area.ROW);
       List<PivotField> columnFields = getFields(PivotField.Area.COLUMN);
       for (List<Object> rowKey : rowKeys) {
     for (List<Object> columnKey : columnKeys) {
        Map<Integer, Object> rowFilter = getFilter(rowFields, rowKey);
        Map<Integer, Object> columnFilter = getFilter(columnFields, columnKey);
        Map<Integer, Object> filter = new HashMap<Integer, Object>();
        filter.putAll(rowFilter);
        filter.putAll(columnFilter);            
        filtersMap.put(rowKey, columnKey, filter);
     }
       }
           
       List<Map<Integer, Object>> tmp = new ArrayList<Map<Integer,Object>>(filtersMap.values());
       Map<Integer, Object>[] filters = new HashMap[tmp.size()];
       filters = tmp.toArray(filters);
       MultiValueMap values = getValues2(dataField, filters);
       for (List<Object> rowKey : rowKeys) {
     for (List<Object> columnKey : columnKeys) {
        List<Object> valuesForFilter = (List<Object>) values.get(filtersMap.get(rowKey, columnKey));
        if (!CollectionUtils.isEmpty(valuesForFilter)) {
    //               System.out.println("filter = " + filter);
    //               System.out.println("values = " + values);
    //               System.out.println(values.size());
           Object summary = PivotUtils.getSummary(dataField, valuesForFilter);
    //               System.out.println("summary = " + summary);
           data.put(rowKey, columnKey, summary);
        }
     }
       }
           
       return data;
    }
    */

    public Tree getColumnsHeaderTree() {
        if (columnsHeaderTree == null) {
            Node root = new Node();
            insertChildren(root, getFields(PivotField.Area.COLUMN));
            columnsHeaderTree = new Tree(root);
        }

        return columnsHeaderTree;
    }

    public Tree getRowsHeaderTree() {
        if (rowsHeaderTree == null) {
            Node root = new Node();
            insertChildren(root, getFields(PivotField.Area.ROW));
            rowsHeaderTree = new Tree(root);
        }

        return rowsHeaderTree;
    }

    public List<List<Object>> getRowKeys() {
        return TreeHelper.getLeafValues(getRowsHeaderTree().getRoot());
    }

    public List<List<Object>> getColumnKeys() {
        return TreeHelper.getLeafValues(getColumnsHeaderTree().getRoot());
    }

    public Object getValueAt(PivotField dataField, List<Object> rowKey, List<Object> columnKey) {
        int index = getFields(PivotField.Area.DATA).indexOf(dataField);
        return calculatedData.get(index).get(rowKey, columnKey);
    }

    public boolean isShowGrandTotalForColumn() {
        return showGrandTotalForColumn;
    }

    public void setShowGrandTotalForColumn(boolean showGrandTotalForColumn) {
        this.showGrandTotalForColumn = showGrandTotalForColumn;
    }

    public boolean isShowGrandTotalForRow() {
        return showGrandTotalForRow;
    }

    public void setShowGrandTotalForRow(boolean showGrandTotalForRow) {
        this.showGrandTotalForRow = showGrandTotalForRow;
    }

    @Override
    public String toString() {
        return "DefaultPivotModel [fields=" + fields + "]";
    }

    private void insertChildren(Node node, List<PivotField> fields) {
        // System.out.println("DefaultPivotModel.insertChildren()");
        Set<Object> values = getPossibleChildrenValues(node, fields);
        if (CollectionUtils.isEmpty(values)) {
            return;
        }

        Iterator<Object> it = values.iterator();
        while (it.hasNext()) {
            node.insert(it.next());
        }

        for (Node child : node.getChildren()) {
            insertChildren(child, fields);
        }
    }

    private Set<Object> getPossibleChildrenValues(Node node, List<PivotField> fields) {
        int level = node.getLevel();
        // System.out.println("level = " + level);
        // System.out.println("fields.size = " + fields.size());
        if (fields.size() <= level) {
            return null;
        }

        PivotField nextField = fields.get(level);
        // System.out.println("nextField = " + nextField);
        Map<Integer, Object> filter = getFilter(fields, node.getPathValues());
        // System.out.println("filter = " + filter);
        Set<Object> values = getUniqueValues(nextField, filter);
        // System.out.println("values = " + values);

        return values;
    }

    /*
     * Retrieves the values for a data field using a filter. The key is the filter map and the
     * value is a list of objects.
     */
    private List<Object> getValues(PivotField field, Map<Integer, Object> filter) {
        //      long start = System.currentTimeMillis();
        List<Object> values = new ArrayList<Object>();
        int fieldIndex = field.getIndex();
        for (int i = 0; i < dataSource.getRowCount(); i++) {
            if (filter.isEmpty()) {
                values.add(dataSource.getValueAt(i, fieldIndex));
            } else {
                if (acceptValue(i, filter)) {
                    values.add(dataSource.getValueAt(i, fieldIndex));
                }
            }
        }
        //      long stop = System.currentTimeMillis();
        //      System.out.println("getValues in " + (stop - start));

        return values;
    }

    /*
     * Nu este eficienta. Obtin timp mai mare pe varianta asta ca fac un loop care acum e de 18.000
     * (in cazul meu de test)
     */
    /*
    private MultiValueMap getValues2(PivotField field, Map<Integer, Object>... filters) {
       System.out.println("||| " + filters.length);
       long start = System.currentTimeMillis();
       MultiValueMap values = new MultiValueMap();
       int fieldIndex = field.getIndex();
       for (int i = 0; i < dataSource.getRowCount(); i++) {
     for (Map<Integer, Object> filter : filters) {
        if (filter.isEmpty()) {
           values.put(filter, dataSource.getValueAt(i, fieldIndex));
        } else {
           if (acceptValue(i, filter)) {
              values.put(filter, dataSource.getValueAt(i, fieldIndex));
           }
        }
     }
       }
       long stop = System.currentTimeMillis();
       System.out.println("getValues in " + (stop - start));
           
       return values;
    }
    */

    /*
     * Retrieves a filter for filtering data source (raw data). The size of fields must be equals with
     * the size of values. The key in map is the field index.  
     */
    private Map<Integer, Object> getFilter(List<PivotField> fields, List<Object> values) {
        //      long start = System.currentTimeMillis();
        Map<Integer, Object> filter = new HashMap<Integer, Object>();
        for (int i = 0; i < values.size(); i++) {
            int fieldIndex = fields.get(i).getIndex();
            // System.out.println(fieldIndex);
            filter.put(fieldIndex, values.get(i));
        }
        //      long stop = System.currentTimeMillis();
        //      System.out.println("getFilter in " + (stop - start));

        return filter;
    }

    private Set<Object> getUniqueValues(PivotField field, Map<Integer, Object> filter) {
        return new TreeSet<Object>(getValues(field, filter));
    }

    /*
    @SuppressWarnings("unchecked")
    private Set<Object> getUniqueValues2(PivotField field, Map<Integer, Object> filter) {
       return new TreeSet<Object>((List<Object>) getValues2(field, filter).get(filter));
    }
    */

    private boolean acceptValue(int row, Map<Integer, Object> filter) {
        boolean accept = true;
        Set<Integer> keys = filter.keySet();
        for (int index : keys) {
            if (!filter.get(index).equals(dataSource.getValueAt(row, fields.get(index)))) {
                // System.out.println(" + " + filter.get(j));
                // System.out.println(" - " + dataSource.getValueAt(i,
                // fields.get(j)));
                return false;
            }
        }

        return accept;
    }

}