| /***************************************************************************** |
| * Copyright (c) 2020 Dirk Fauth. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation |
| * |
| *****************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.test.performance; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor; |
| import org.eclipse.nebula.widgets.nattable.data.ListDataProvider; |
| import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor; |
| import org.eclipse.nebula.widgets.nattable.dataset.person.Person; |
| import org.eclipse.nebula.widgets.nattable.dataset.person.Person.Gender; |
| import org.eclipse.nebula.widgets.nattable.dataset.person.PersonService; |
| import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer; |
| import org.eclipse.nebula.widgets.nattable.layer.DataLayer; |
| import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; |
| import org.eclipse.nebula.widgets.nattable.tree.ITreeData; |
| import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel; |
| import org.eclipse.nebula.widgets.nattable.tree.TreeLayer; |
| import org.eclipse.nebula.widgets.nattable.tree.TreeRowModel; |
| import org.junit.Test; |
| |
| public class TreeLayerPerformanceTest { |
| |
| @Test |
| public void performanceCollapseExpandAll() { |
| List<Person> values = PersonService.getFixedPersons(); |
| for (int i = 42; i < 100_042; i++) { |
| values.add(new Person(i, "Ralph", "Wiggum", Gender.MALE, false, new Date())); |
| } |
| |
| String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" }; |
| IColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(propertyNames); |
| ListDataProvider<Person> bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor); |
| DataLayer bodyDataLayer = new DataLayer(bodyDataProvider); |
| |
| ITreeRowModel<Person> treeRowModel = new TreeRowModel<>(new PersonTreeData(values)); |
| |
| RowHideShowLayer hideShowLayer = new RowHideShowLayer(bodyDataLayer); |
| |
| SelectionLayer selectionLayer = new SelectionLayer(hideShowLayer); |
| |
| TreeLayer treeLayer = new TreeLayer(selectionLayer, treeRowModel); |
| |
| long start = System.currentTimeMillis(); |
| treeLayer.collapseAll(); |
| long end = System.currentTimeMillis(); |
| |
| assertEquals(3, treeLayer.getRowCount()); |
| System.out.println("collapseAll() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| start = System.currentTimeMillis(); |
| treeLayer.expandAll(); |
| end = System.currentTimeMillis(); |
| |
| assertEquals(100_018, treeLayer.getRowCount()); |
| System.out.println("expandAll() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| @Test |
| public void performanceCollapseAllExpandOneBigParent() { |
| List<Person> values = PersonService.getFixedPersons(); |
| for (int i = 42; i < 100_042; i++) { |
| values.add(new Person(i, "Ralph", "Wiggum", Gender.MALE, false, new Date())); |
| } |
| |
| String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" }; |
| IColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(propertyNames); |
| ListDataProvider<Person> bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor); |
| DataLayer bodyDataLayer = new DataLayer(bodyDataProvider); |
| |
| ITreeRowModel<Person> treeRowModel = new TreeRowModel<>(new PersonTreeData(values)); |
| |
| RowHideShowLayer hideShowLayer = new RowHideShowLayer(bodyDataLayer); |
| |
| SelectionLayer selectionLayer = new SelectionLayer(hideShowLayer); |
| |
| TreeLayer treeLayer = new TreeLayer(selectionLayer, treeRowModel); |
| |
| long start = System.currentTimeMillis(); |
| treeLayer.collapseAll(); |
| long end = System.currentTimeMillis(); |
| |
| assertEquals(3, treeLayer.getRowCount()); |
| System.out.println("collapseAll() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| start = System.currentTimeMillis(); |
| treeLayer.expandTreeRow(18); |
| end = System.currentTimeMillis(); |
| |
| assertEquals(100_002, treeLayer.getRowCount()); |
| System.out.println("expandRow() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| @Test |
| public void performanceBuildCache() { |
| List<Person> values = PersonService.getFixedPersons(); |
| for (int i = 42; i < 1_000_042; i++) { |
| values.add(new Person(i, "Ralph", "Wiggum", Gender.MALE, false, new Date())); |
| } |
| |
| String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" }; |
| IColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(propertyNames); |
| ListDataProvider<Person> bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor); |
| DataLayer bodyDataLayer = new DataLayer(bodyDataProvider); |
| |
| ITreeRowModel<Person> treeRowModel = new TreeRowModel<>(new PersonTreeData(values)); |
| |
| SelectionLayer selectionLayer = new SelectionLayer(bodyDataLayer); |
| |
| TreeLayer treeLayer = new TreeLayer(selectionLayer, treeRowModel); |
| |
| treeLayer.collapseTreeRow(0); |
| |
| long start = System.currentTimeMillis(); |
| assertEquals(1_000_011, treeLayer.getRowCount()); |
| long end = System.currentTimeMillis(); |
| |
| System.out.println("buildCache() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private static class PersonTreeData implements ITreeData<Person> { |
| |
| private List<Person> values; |
| |
| private Map<String, List<Person>> parentMapping; |
| |
| private Map<String, Person> firstElementMapping = new HashMap<>(); |
| |
| public PersonTreeData(List<Person> values) { |
| this.values = values; |
| |
| // first we need to sort by lastname to ensure all elements with the |
| // same lastname are grouped together |
| this.values.sort(Comparator.comparing(Person::getLastName)); |
| |
| // then we build up the mapping from lastname to all child elements |
| this.parentMapping = values.stream().collect(Collectors.groupingBy(Person::getLastName)); |
| |
| // identify the parent node element |
| String current = null; |
| for (Person p : this.values) { |
| if (p.getLastName() != current) { |
| this.firstElementMapping.put(p.getLastName(), p); |
| current = p.getLastName(); |
| } |
| } |
| |
| // remove the parent node element from the children list |
| this.firstElementMapping.forEach((lastname, parent) -> { |
| this.parentMapping.get(lastname).remove(parent); |
| }); |
| } |
| |
| @Override |
| public int getDepthOfData(Person object) { |
| Person firstElement = this.firstElementMapping.get(object.getLastName()); |
| return firstElement.equals(object) ? 0 : 1; |
| } |
| |
| @Override |
| public int getDepthOfData(int index) { |
| return getDepthOfData(getDataAtIndex(index)); |
| } |
| |
| @Override |
| public Person getDataAtIndex(int index) { |
| if (!isValidIndex(index)) { |
| return null; |
| } |
| return this.values.get(index); |
| } |
| |
| @Override |
| public int indexOf(Person child) { |
| return this.values.indexOf(child); |
| } |
| |
| @Override |
| public boolean hasChildren(Person object) { |
| if (object != null && getDepthOfData(object) == 0) { |
| List<Person> children = this.parentMapping.get(object.getLastName()); |
| return children != null && !children.isEmpty(); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean hasChildren(int index) { |
| return hasChildren(getDataAtIndex(index)); |
| } |
| |
| @Override |
| public List<Person> getChildren(Person object) { |
| if (object != null && getDepthOfData(object) == 0) { |
| return this.parentMapping.get(object.getLastName()); |
| } |
| return new ArrayList<>(0); |
| } |
| |
| @Override |
| public List<Person> getChildren(Person object, boolean fullDepth) { |
| // since we only support one level here it is the same as |
| // getChildren(PersonWithAddress) |
| return getChildren(object); |
| } |
| |
| @Override |
| public List<Person> getChildren(int index) { |
| return getChildren(getDataAtIndex(index)); |
| } |
| |
| @Override |
| public int getElementCount() { |
| return this.values.size(); |
| } |
| |
| @Override |
| public boolean isValidIndex(int index) { |
| return (!(index < 0) && index < this.values.size()); |
| } |
| |
| } |
| |
| } |