Java tutorial
/* * 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; } }