In recent weeks, JunitTest_toolkit_complex was seen occasionally
failing in CI, see e.g. last ESC minutes [1]:
> + tests that failed more than twice in last seven days
> [...]
> 18 JunitTest_toolkit_complex gerrit_linux_clang_dbgutil
It's unclear why the failure started to happen recently
and it doesn't happen reliably.
I was unable to reproduce the problem locally, even when running
make JunitTest_toolkit_complex
in a loop.
From a sample Jenkins failure log [2], what *might* be happening
is that the model etc. get disposed before the grid control in
the Java test code.
From the console log:
There were 6 failures:
1) testView(complex.toolkit.GridControl)
com.sun.star.lang.DisposedException
at com.sun.star.lib.uno.environments.remote.JobQueue.removeJob(JobQueue.java:199)
at com.sun.star.lib.uno.environments.remote.JobQueue.enter(JobQueue.java:309)
at com.sun.star.lib.uno.environments.remote.JobQueue.enter(JobQueue.java:279)
at com.sun.star.lib.uno.environments.remote.JavaThreadPool.enter(JavaThreadPool.java:83)
at com.sun.star.lib.uno.bridges.java_remote.java_remote_bridge.sendRequest(java_remote_bridge.java:619)
at com.sun.star.lib.uno.bridges.java_remote.ProxyFactory$Handler.request(ProxyFactory.java:145)
at com.sun.star.lib.uno.bridges.java_remote.ProxyFactory$Handler.invoke(ProxyFactory.java:129)
at jdk.proxy2/jdk.proxy2.$Proxy21.dispose(Unknown Source)
at complex.toolkit.GridControl.impl_dispose(GridControl.java:79)
at complex.toolkit.GridControl.cleanupTestCase(GridControl.java:615)
... 35 trimmed
Caused by: java.io.EOFException
at java.base/java.io.DataInputStream.readInt(DataInputStream.java:386)
at com.sun.star.lib.uno.protocols.urp.urp.readBlock(urp.java:364)
at com.sun.star.lib.uno.protocols.urp.urp.readMessage(urp.java:96)
at com.sun.star.lib.uno.bridges.java_remote.java_remote_bridge$MessageDispatcher.run(java_remote_bridge.java:92)
2) testSortableDataModel(complex.toolkit.GridControl)
com.sun.star.lang.DisposedException: java_remote_bridge com.sun.star.lib.uno.bridges.java_remote.java_remote_bridge@210366b4 is disposed
at com.sun.star.lib.uno.bridges.java_remote.java_remote_bridge.checkDisposed(java_remote_bridge.java:689)
at com.sun.star.lib.uno.bridges.java_remote.java_remote_bridge.sendRequest(java_remote_bridge.java:603)
at com.sun.star.lib.uno.bridges.java_remote.ProxyFactory$Handler.request(ProxyFactory.java:145)
at com.sun.star.lib.uno.bridges.java_remote.ProxyFactory$Handler.invoke(ProxyFactory.java:129)
at jdk.proxy2/jdk.proxy2.$Proxy12.getServiceManager(Unknown Source)
at complex.toolkit.GridControl.createInstance(GridControl.java:654)
at complex.toolkit.GridControl.impl_recreateGridModel(GridControl.java:91)
at complex.toolkit.GridControl.testSortableDataModel(GridControl.java:354)
C++ backtrace:
Thread 1 (Thread 0x7f0aff59adc0 (LWP 2939300)):
#0 0x00007f0b0708d4bc in __pthread_kill_implementation () at /lib64/libc.so.6
#1 0x00007f0b0703fbb6 in raise () at /lib64/libc.so.6
#2 0x00007f0b07029873 in abort () at /lib64/libc.so.6
#3 0x00007f0b0786db51 in (anonymous namespace)::callSystemHandler(int, siginfo_t*, void*) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libuno_sal.so.3
#4 0x00007f0b0786d680 in (anonymous namespace)::signalHandlerFunction(int, siginfo_t*, void*) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libuno_sal.so.3
#5 0x00007f0b0703fc60 in <signal handler called> () at /lib64/libc.so.6
#6 0x00007f0b0708d4bc in __pthread_kill_implementation () at /lib64/libc.so.6
#7 0x00007f0b0703fbb6 in raise () at /lib64/libc.so.6
#8 0x00007f0b07029873 in abort () at /lib64/libc.so.6
#9 0x00007f0b0702979b in _nl_load_domain.cold () at /lib64/libc.so.6
#10 0x00007f0b070388f6 in () at /lib64/libc.so.6
#11 0x00007f0aff09e392 in com::sun::star::uno::Reference<com::sun::star::awt::grid::XMutableGridDataModel>::operator->() const () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libtklo.so
#12 0x00007f0aff0961a1 in (anonymous namespace)::SortableGridDataModel::getRowCount() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libtklo.so
#13 0x00007f0aff1dae5c in svt::table::UnoControlTableModel::getRowCount() const () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libtklo.so
#14 0x00007f0aff1312b5 in svt::table::TableControl_Impl::doPaintContent(OutputDevice&, tools::Rectangle const&) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libtklo.so
#15 0x00007f0aff149fc9 in svt::table::TableDataWindow::Paint(OutputDevice&, tools::Rectangle const&) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libtklo.so
#16 0x00007f0afb41f321 in PaintHelper::DoPaint(vcl::Region const*) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#17 0x00007f0afb42074f in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#18 0x00007f0afb41ff57 in PaintHelper::~PaintHelper() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#19 0x00007f0afb4207d6 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#20 0x00007f0afb41ff57 in PaintHelper::~PaintHelper() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#21 0x00007f0afb4207d6 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#22 0x00007f0afb41ff57 in PaintHelper::~PaintHelper() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#23 0x00007f0afb4207d6 in vcl::Window::ImplCallPaint(vcl::Region const*, ImplPaintFlags) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#24 0x00007f0afb420c7e in vcl::Window::ImplCallOverlapPaint() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#25 0x00007f0afb420e34 in vcl::Window::ImplHandlePaintHdl(Timer*) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#26 0x00007f0afb420cbd in vcl::Window::LinkStubImplHandlePaintHdl(void*, Timer*) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#27 0x00007f0afbe37178 in Link<Timer*, void>::Call(Timer*) const () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#28 0x00007f0afbe36fcc in Timer::Invoke() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#29 0x00007f0afbdd92f9 in Scheduler::CallbackTaskScheduling() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#30 0x00007f0afc32ee86 in SalTimer::CallCallback() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#31 0x00007f0afc32ce9e in SvpSalInstance::CheckTimeout(bool) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#32 0x00007f0afc32e07d in SvpSalInstance::ImplYield(bool, bool) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#33 0x00007f0afc32e678 in SvpSalInstance::DoYield(bool, bool) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#34 0x00007f0afbe08bfc in InnerYield(bool, bool) () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#35 0x00007f0afbe083cb in Application::Yield() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#36 0x00007f0afbe08167 in Application::Execute() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#37 0x00007f0b0744e27a in desktop::Desktop::Main() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libsofficeapp.so
#38 0x00007f0afbe33c83 in ImplSVMainRun() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#39 0x00007f0afbd2ff31 in SalInstance::SVMainRun() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#40 0x00007f0afbe33082 in ImplSVMain() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#41 0x00007f0afbe34ee9 in SVMain() () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libvcllo.so
#42 0x00007f0b074d143c in soffice_main () at /home/tdf/lode/jenkins/workspace/lo_gerrit/Config/linux_clang_dbgutil_64/instdir/program/libsofficeapp.so
#43 0x000055da4f9609bd in sal_main () at desktop/source/app/main.c:51
ret = 8
#44 0x000055da4f960997 in main (argc=8, argv=0x7ffe8beae9c8) at desktop/source/app/main.c:49
ret = 538976288
This looks like event processing might have been triggered that
triggers the (re)paint of the TableDataWindow/TableControl_Impl
(frames #15 and #14), but then dereferencing the UnoControlTableModel's
data model in frame #11 fails, maybe because the data model has already
been disposed earlier?
Dispose the grid control at the end of the testView() Java test that is
the one that creates it and the only one using it, so that happens
before the model and data model etc. get disposed in impl_dispose() that gets
called from cleanupTestCase().
That is to prevent that a repaint is still attempted.
(When the TableControl is disposed, it's model is unset,
so svt::table::TableControl_Impl::doPaintContent (frame #14
in the backtrace) will return early, before trying to call
svt::table::UnoControlTableModel::getRowCount that is seen
in frame #13 of the above backtrace.)
Note: The above backtrace does not yet include the commits
refactoring TableControl and TableControl_Impl like
commit 39749ff5b4
Author: Michael Weghorn <m.weghorn@posteo.de>
Date: Fri Sep 18 15:30:10 2026 +0200
toolkit: Merge TableControl into TableControl_Impl
and
commit b580576a4d
Author: Michael Weghorn <m.weghorn@posteo.de>
Date: Fri Sep 18 15:44:17 2026 +0200
toolkit: Rename TableControl_Impl to TableControl
and
commit e4f1a48967
Author: Michael Weghorn <m.weghorn@posteo.de>
Date: Fri Sep 18 15:48:54 2026 +0200
toolkit: Rename TableControl header/source to match new class name
, so the naming and exact code differs slightly with current master,
but as far as I can tell, that otherwise shouldn't have any effect on
the observed behavior.
This is an attempt to address the JunitTest_toolkit_complex failures
mentioned above, without being able to actually verify that locally
(as the failure could not be reproduced locally either).
[1] https://lists.freedesktop.org/archives/libreoffice/2026-September/094885.html
[2] https://ci.libreoffice.org/job/gerrit_linux_clang_dbgutil/212155/console
Change-Id: If1a5411c92dfdfb49a34b31f4e44ac47d409772c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211231
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Tested-by: Jenkins
749 lines
34 KiB
Java
749 lines
34 KiB
Java
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* 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 .
|
|
*/
|
|
|
|
package complex.toolkit;
|
|
|
|
import com.sun.star.awt.XControl;
|
|
import com.sun.star.awt.XControlContainer;
|
|
import com.sun.star.awt.XControlModel;
|
|
import com.sun.star.awt.XToolkit;
|
|
import com.sun.star.awt.grid.DefaultGridDataModel;
|
|
import com.sun.star.awt.grid.XGridColumn;
|
|
import com.sun.star.awt.grid.XGridColumnModel;
|
|
import com.sun.star.awt.grid.XGridControl;
|
|
import com.sun.star.awt.grid.XGridDataModel;
|
|
import com.sun.star.awt.grid.XMutableGridDataModel;
|
|
import com.sun.star.awt.grid.XSortableMutableGridDataModel;
|
|
import com.sun.star.beans.Pair;
|
|
import com.sun.star.beans.XPropertySet;
|
|
import com.sun.star.container.ContainerEvent;
|
|
import com.sun.star.container.XContainerListener;
|
|
import com.sun.star.container.XNameContainer;
|
|
import com.sun.star.lang.EventObject;
|
|
import com.sun.star.lang.IndexOutOfBoundsException;
|
|
import com.sun.star.lang.XComponent;
|
|
import com.sun.star.lang.XEventListener;
|
|
import com.sun.star.lang.XMultiServiceFactory;
|
|
import com.sun.star.uno.Exception;
|
|
import com.sun.star.uno.UnoRuntime;
|
|
import com.sun.star.uno.XComponentContext;
|
|
import com.sun.star.uno.XInterface;
|
|
import com.sun.star.util.XCloneable;
|
|
import complex.toolkit.awtgrid.DummyColumn;
|
|
import complex.toolkit.awtgrid.TMutableGridDataModel;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
import org.junit.AfterClass;
|
|
import org.junit.BeforeClass;
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import static org.junit.Assert.*;
|
|
import org.openoffice.test.OfficeConnection;
|
|
|
|
/** is a unit test for the grid control related implementations
|
|
*/
|
|
public class GridControl
|
|
{
|
|
|
|
public GridControl()
|
|
{
|
|
m_context = m_connection.getComponentContext();
|
|
}
|
|
|
|
|
|
private static void impl_dispose( final Object... i_components )
|
|
{
|
|
for ( int i=0; i<i_components.length; ++i )
|
|
{
|
|
if ( i_components[i] != null )
|
|
{
|
|
final XComponent component = UnoRuntime.queryInterface( XComponent.class, i_components[i] );
|
|
component.dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void impl_recreateGridModel() throws Exception
|
|
{
|
|
impl_dispose( m_gridControlModel, m_columnModel, m_dataModel );
|
|
|
|
// create a grid control model, and ensure it has a proper data and column model already
|
|
m_gridControlModel = UnoRuntime.queryInterface( XPropertySet.class,
|
|
createInstance( "com.sun.star.awt.grid.UnoControlGridModel" ) );
|
|
assertNotNull( "grid control model does not provide XPropertySet interface", m_gridControlModel );
|
|
|
|
// ensure that the model has default column/data models
|
|
m_columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, m_gridControlModel.getPropertyValue( "ColumnModel" ) );
|
|
assertNotNull( "the control model is expected to have an initial column model", m_columnModel );
|
|
final XGridDataModel dataModel = UnoRuntime.queryInterface( XGridDataModel.class, m_gridControlModel.getPropertyValue( "GridDataModel" ) );
|
|
assertNotNull( "the control model is expected to have an initial data model", dataModel );
|
|
m_dataModel = UnoRuntime.queryInterface( XSortableMutableGridDataModel.class,
|
|
dataModel );
|
|
assertNotNull( "the out-of-the-box data model should be mutable and sortable", m_dataModel );
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testGridControlCloning() throws Exception
|
|
{
|
|
impl_recreateGridModel();
|
|
|
|
// give the test something to compare, actually
|
|
XGridColumnModel columnModel = UnoRuntime.queryInterface( XGridColumnModel.class,
|
|
m_gridControlModel.getPropertyValue( "ColumnModel" ) );
|
|
columnModel.setDefaultColumns( 10 );
|
|
|
|
// clone the grid model
|
|
final XCloneable cloneable = UnoRuntime.queryInterface( XCloneable.class, m_gridControlModel );
|
|
assertNotNull( "all UnoControlModel's are expected to be clonable", cloneable );
|
|
|
|
final XInterface clone = cloneable.createClone();
|
|
final XPropertySet clonedProps = UnoRuntime.queryInterface( XPropertySet.class, clone );
|
|
|
|
// TODO: check all those generic properties for equality
|
|
|
|
// the data model and the column model should have been cloned, too
|
|
// in particular, the clone should not share the sub models with the original
|
|
final XMutableGridDataModel originalDataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class,
|
|
m_gridControlModel.getPropertyValue( "GridDataModel" ) );
|
|
final XMutableGridDataModel clonedDataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class,
|
|
clonedProps.getPropertyValue( "GridDataModel" ) );
|
|
assertFalse( "data model should not be shared after cloning", UnoRuntime.areSame( originalDataModel, clonedDataModel ) );
|
|
impl_assertEquality( originalDataModel, clonedDataModel );
|
|
|
|
final XGridColumnModel originalColumnModel = columnModel;
|
|
final XGridColumnModel clonedColumnModel = UnoRuntime.queryInterface( XGridColumnModel.class,
|
|
clonedProps.getPropertyValue( "ColumnModel" ) );
|
|
assertFalse( "column model should not be shared after cloning", UnoRuntime.areSame( originalColumnModel, clonedColumnModel ) );
|
|
impl_assertEquality( originalColumnModel, clonedColumnModel );
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testDisposal() throws Exception
|
|
{
|
|
impl_recreateGridModel();
|
|
|
|
final int columnCount = 3;
|
|
m_columnModel.setDefaultColumns( columnCount );
|
|
|
|
// add disposal listeners to all columns so far
|
|
final XGridColumn[] columns = m_columnModel.getColumns();
|
|
assertEquals( "creating default columns resulted in unexpected column count", columnCount, columns.length );
|
|
final DisposeListener[] columnListeners = new DisposeListener[columnCount];
|
|
for ( int i=0; i<columnCount; ++i )
|
|
columnListeners[i] = new DisposeListener( columns[i] );
|
|
|
|
// add another column, and check that upon removal, it is disposed
|
|
final int newColumnIndex = m_columnModel.addColumn( m_columnModel.createColumn() );
|
|
final DisposeListener columnListener = new DisposeListener( m_columnModel.getColumn( newColumnIndex ) );
|
|
m_columnModel.removeColumn( newColumnIndex );
|
|
assertTrue( "explicit column removal is expected to dispose the column", columnListener.isDisposed() );
|
|
|
|
// by definition, the grid control model is the owner of both the column and the data model. So, setting
|
|
// a new column/data model should implicitly dispose the old models
|
|
final DisposeListener oldDataModelListener = new DisposeListener( m_dataModel );
|
|
final DisposeListener oldColumnModelListener = new DisposeListener( m_columnModel );
|
|
|
|
final Object newDataModel = createInstance( "com.sun.star.awt.grid.DefaultGridDataModel" );
|
|
final Object newColumnModel = createInstance( "com.sun.star.awt.grid.DefaultGridColumnModel" );
|
|
final DisposeListener newDataModelListener = new DisposeListener( newDataModel );
|
|
final DisposeListener newColumnModelListener = new DisposeListener( newColumnModel );
|
|
|
|
m_gridControlModel.setPropertyValue( "GridDataModel", newDataModel );
|
|
assertTrue( "setting a new data model failed", impl_areSameInterface( newDataModel, m_gridControlModel.getPropertyValue( "GridDataModel" ) ) );
|
|
m_gridControlModel.setPropertyValue( "ColumnModel", newColumnModel );
|
|
assertTrue( "setting a new column model failed", impl_areSameInterface( newColumnModel, m_gridControlModel.getPropertyValue( "ColumnModel" ) ) );
|
|
|
|
assertTrue( "old data model has not been disposed", oldDataModelListener.isDisposed() );
|
|
assertTrue( "old column model has not been disposed", oldColumnModelListener.isDisposed() );
|
|
for ( int i=0; i<columnCount; ++i )
|
|
assertTrue( "column no. " + i + " has not been disposed", columnListeners[i].isDisposed() );
|
|
|
|
// the same holds if the grid control model itself is disposed - it should dispose the depending models, too
|
|
assertFalse( "new data model is already disposed - this is unexpected", newDataModelListener.isDisposed() );
|
|
assertFalse( "new column model is already disposed - this is unexpected", newColumnModelListener.isDisposed() );
|
|
impl_dispose( m_gridControlModel );
|
|
assertTrue( "new data model is not disposed after disposing the grid column model", newDataModelListener.isDisposed() );
|
|
assertTrue( "new column model is not disposed after disposing the grid column model", newColumnModelListener.isDisposed() );
|
|
}
|
|
|
|
|
|
/**
|
|
* tests various aspects of the <code>XMutableGridDataModel</code> interface
|
|
*/
|
|
@Test
|
|
public void testMutableGridDataModel() throws Exception
|
|
{
|
|
impl_recreateGridModel();
|
|
|
|
TMutableGridDataModel test = new TMutableGridDataModel( m_dataModel );
|
|
test.testAddRow();
|
|
test.testAddRows();
|
|
test.testInsertRow();
|
|
test.testInsertRows();
|
|
test.testRemoveRow();
|
|
test.testRemoveAllRows();
|
|
test.testUpdateCellData();
|
|
test.testUpdateRowData();
|
|
test.testUpdateRowHeading();
|
|
test.cleanup();
|
|
|
|
// a somewhat less straight-forward test: the data model is expected to implicitly increase its column count
|
|
// when you add a row which has more columns than currently known
|
|
final XMutableGridDataModel dataModel = DefaultGridDataModel.create( m_context );
|
|
dataModel.addRow( 0, new Object[] { 1 } );
|
|
assertEquals( "unexpected column count after adding the most simple row", 1, dataModel.getColumnCount() );
|
|
dataModel.addRow( 1, new Object[] { 1, 2 } );
|
|
assertEquals( "implicit extension of the column count doesn't work", 2, dataModel.getColumnCount() );
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testGridColumnModel() throws Exception
|
|
{
|
|
impl_recreateGridModel();
|
|
|
|
ColumnModelListener listener = new ColumnModelListener();
|
|
m_columnModel.addContainerListener( listener );
|
|
|
|
// insert default columns into the previously empty model, ensure we get the right notifications
|
|
final int defaultColumnsCount = 3;
|
|
m_columnModel.setDefaultColumns( defaultColumnsCount );
|
|
impl_assertColumnModelConsistency();
|
|
List< ContainerEvent > events = listener.assertExclusiveInsertionEvents();
|
|
listener.reset();
|
|
assertEquals( "wrong number of events fired by setDefaultColumns", defaultColumnsCount, events.size() );
|
|
for ( int i=0; i<defaultColumnsCount; ++i )
|
|
{
|
|
final ContainerEvent event = events.get(i);
|
|
final int index = impl_assertInteger( event.Accessor );
|
|
assertEquals( "unexpected Accessor value in insert notification", i, index );
|
|
assertTrue( "wrong column object notified in insert notification",
|
|
impl_areSameInterface( event.Element, m_columnModel.getColumn(i) ) );
|
|
}
|
|
|
|
// insert some more default columns, ensure that all previously existing columns are removed
|
|
final int moreDefaultColumnsCount = 5;
|
|
m_columnModel.setDefaultColumns( moreDefaultColumnsCount );
|
|
impl_assertColumnModelConsistency();
|
|
assertEquals( "setting default columns is expected to remove all previously existing columns",
|
|
moreDefaultColumnsCount, m_columnModel.getColumnCount() );
|
|
|
|
// in this situation, both removal and insertion events have been notified
|
|
final List< ContainerEvent > removalEvents = listener.getRemovalEvents();
|
|
final List< ContainerEvent > insertionEvents = listener.getInsertionEvents();
|
|
listener.reset();
|
|
|
|
// for the removal events, check the indexes
|
|
assertEquals( "wrong number of columns removed (or notified) upon setting default columns",
|
|
defaultColumnsCount, removalEvents.size() );
|
|
for ( int i=0; i<removalEvents.size(); ++i )
|
|
{
|
|
final ContainerEvent event = removalEvents.get(i);
|
|
final int removedIndex = impl_assertInteger( event.Accessor );
|
|
|
|
// The implementation is allowed to remove the columns from the beginning, in which case the
|
|
// index of the removed column must always be 0, since e.g. the second column has index 0
|
|
// after the first column (which previously had index 0) had been removed.
|
|
// Alternatively, the implementation is allowed to remove columns from the end, which means
|
|
// that the column index given in the event is steadily increasing.
|
|
assertTrue( "unexpected column removal event column index",
|
|
( removedIndex == 0 ) || ( removedIndex == removalEvents.size() - 1 - i ) );
|
|
}
|
|
|
|
// for the insertion events, check the indexes as well
|
|
assertEquals( "wrong number of insertion events when setting default columns over existing columns",
|
|
moreDefaultColumnsCount, insertionEvents.size() );
|
|
for ( int i=0; i<insertionEvents.size(); ++i )
|
|
{
|
|
final ContainerEvent event = insertionEvents.get(i);
|
|
final int index = impl_assertInteger( event.Accessor );
|
|
assertEquals( i, index );
|
|
}
|
|
|
|
// okay, remove all those columns
|
|
while ( m_columnModel.getColumnCount() != 0 )
|
|
{
|
|
final int columnCount = m_columnModel.getColumnCount();
|
|
final int removeColumnIndex = m_randomGenerator.nextInt( columnCount );
|
|
m_columnModel.removeColumn( removeColumnIndex );
|
|
events = listener.assertExclusiveRemovalEvents();
|
|
listener.reset();
|
|
assertEquals( "removing a single column should notify a single event", 1, events.size() );
|
|
final ContainerEvent event = events.get(0);
|
|
final int removalIndex = impl_assertInteger( event.Accessor );
|
|
assertEquals( "removing an arbitrary column does not notify the proper accessor",
|
|
removeColumnIndex, removalIndex );
|
|
}
|
|
|
|
// calling addColumn with a column not created by the given model/implementation should not succeed
|
|
boolean caughtExpected = false;
|
|
try
|
|
{
|
|
m_columnModel.addColumn( new DummyColumn() );
|
|
}
|
|
catch( final com.sun.star.lang.IllegalArgumentException e )
|
|
{
|
|
assertTrue( impl_areSameInterface( e.Context, m_columnModel ) );
|
|
caughtExpected = true;
|
|
}
|
|
assertTrue( "adding a dummy (self-implemented) grid column to the model should not succeed", caughtExpected );
|
|
|
|
// adding a single column to the end should succeed, properly notify, and still be consistent
|
|
final XGridColumn newColumn = m_columnModel.createColumn();
|
|
m_columnModel.addColumn( newColumn );
|
|
impl_assertColumnModelConsistency();
|
|
events = listener.assertExclusiveInsertionEvents();
|
|
listener.reset();
|
|
assertEquals( "addColumn notifies the wrong number of insertion events", 1, events.size() );
|
|
final int insertionIndex = impl_assertInteger( events.get(0).Accessor );
|
|
assertEquals( insertionIndex, newColumn.getIndex() );
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testDataModel() throws Exception
|
|
{
|
|
impl_recreateGridModel();
|
|
|
|
// ensure that getCellData and getRowData have the same opinion on the data they deliver
|
|
final Object[][] data = new Object[][] {
|
|
new Object[] { 15, 17, 0 },
|
|
new Object[] { 9, 8, 14 },
|
|
new Object[] { 17, 2, 16 },
|
|
new Object[] { 0, 7, 14 },
|
|
new Object[] { 10, 16, 16 },
|
|
};
|
|
m_dataModel.addRows( new Object[ data.length ], data );
|
|
|
|
for ( int row = 0; row < data.length; ++row )
|
|
{
|
|
assertArrayEquals( "getRowData delivers wrong data in row " + row, data[row], m_dataModel.getRowData( row ) );
|
|
for ( int col = 0; col < data[row].length; ++col )
|
|
{
|
|
assertEquals( "getCellData delivers wrong data at position (" + col + ", " + row + ")",
|
|
data[row][col], m_dataModel.getCellData( col, row ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testSortableDataModel() throws Exception
|
|
{
|
|
impl_recreateGridModel();
|
|
|
|
final int colCount = 3;
|
|
final int rowCount = 10;
|
|
// initialize with some data
|
|
final Object[][] data = new Object[][] {
|
|
new Object[] { 15, 17, 0 },
|
|
new Object[] { 9, 8, 14 },
|
|
new Object[] { 17, 2, 16 },
|
|
new Object[] { 0, 7, 14 },
|
|
new Object[] { 10, 16, 16 },
|
|
new Object[] { 2, 8, 10 },
|
|
new Object[] { 4, 8, 3 },
|
|
new Object[] { 7, 9, 0 },
|
|
new Object[] { 15, 6, 19 },
|
|
new Object[] { 2, 14, 19 }
|
|
};
|
|
final Object[] rowHeadings = new Object[] {
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
};
|
|
// ensure consistency of the test data
|
|
assertEquals( rowHeadings.length, rowCount );
|
|
assertEquals( data.length, rowCount );
|
|
for ( Object[] rowData : data )
|
|
assertEquals( rowData.length, colCount );
|
|
|
|
// add the test data
|
|
m_dataModel.addRows( rowHeadings, data );
|
|
assertEquals( rowCount, m_dataModel.getRowCount() );
|
|
assertEquals( colCount, m_dataModel.getColumnCount() );
|
|
|
|
// sort by each column
|
|
for ( int colIndex = 0; colIndex < colCount; ++colIndex )
|
|
{
|
|
for ( boolean ascending : new boolean[] { true, false } )
|
|
{
|
|
m_dataModel.sortByColumn( colIndex, ascending );
|
|
Pair<Integer,Boolean> currentSortOrder = m_dataModel.getCurrentSortOrder();
|
|
assertEquals( "invalid current sort column (column " + colIndex + ")", currentSortOrder.First.intValue(), colIndex );
|
|
assertEquals( "invalid current sort direction", currentSortOrder.Second.booleanValue(), ascending );
|
|
|
|
/*for ( int i=0; i<rowCount; ++i )
|
|
{
|
|
for ( int j=0; j<colCount; ++j )
|
|
System.out.print( m_dataModel.getCellData( j, i ).toString() + ", " );
|
|
System.out.println();
|
|
}*/
|
|
|
|
// verify the data is actually sorted by this column
|
|
for ( int rowIndex = 0; rowIndex < rowCount - 1; ++rowIndex )
|
|
{
|
|
final Object currentValue = m_dataModel.getCellData( colIndex, rowIndex );
|
|
final int currentIntValue = impl_assertInteger( currentValue );
|
|
final Object nextValue = m_dataModel.getCellData( colIndex, rowIndex + 1 );
|
|
final int nextIntValue = impl_assertInteger( nextValue );
|
|
assertTrue( "data in row " + rowIndex + " is actually not sorted " + ( ascending ? "ascending" : "descending" ),
|
|
ascending ? currentIntValue <= nextIntValue
|
|
: currentIntValue >= nextIntValue );
|
|
|
|
// ensure the data in the other columns, and the row headings, are sorted as well
|
|
final Object rowHeading = m_dataModel.getRowHeading( rowIndex );
|
|
final int unsortedRowIndex = impl_assertInteger( rowHeading );
|
|
for ( int innerColIndex = 0; innerColIndex < colCount; ++innerColIndex )
|
|
{
|
|
assertEquals( "sorted row " + rowIndex + ", unsorted row " + unsortedRowIndex + ", col " + innerColIndex +
|
|
": wrong data",
|
|
data[unsortedRowIndex][innerColIndex], m_dataModel.getCellData( innerColIndex, rowIndex ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
public void testView() throws Exception
|
|
{
|
|
final XControl control = impl_createDialogWithGridControl();
|
|
final XPropertySet gridModelProps =
|
|
UnoRuntime.queryInterface( XPropertySet.class, control.getModel() );
|
|
|
|
// in the current implementation (not sure this is a good idea at all), the control (more precise: the peer)
|
|
// ensures that if there are no columns in the column model, but in the data model, then the column model
|
|
// will implicitly have the needed columns added.
|
|
// To ensure that clients which rely on this do not break in the future, check this here.
|
|
final XMutableGridDataModel dataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class,
|
|
gridModelProps.getPropertyValue( "GridDataModel" ) );
|
|
assertNotNull( dataModel );
|
|
assertEquals( 0, dataModel.getColumnCount() );
|
|
|
|
final XGridColumnModel columnModel = UnoRuntime.queryInterface( XGridColumnModel.class,
|
|
gridModelProps.getPropertyValue( "ColumnModel" ) );
|
|
assertNotNull( columnModel );
|
|
assertEquals( 0, columnModel.getColumnCount() );
|
|
|
|
final int columnCount = 3;
|
|
final int rowCount = 2;
|
|
dataModel.addRow( null, new Object[] { 1, 2, 3 } );
|
|
dataModel.addRow( null, new Object[] { 6, 5, 4 } );
|
|
|
|
assertEquals( columnCount, dataModel.getColumnCount() );
|
|
assertEquals( columnCount, columnModel.getColumnCount() );
|
|
|
|
// some cursor traveling
|
|
final XGridControl gridControl = UnoRuntime.queryInterface( XGridControl.class, control );
|
|
gridControl.goToCell( 0, 0 );
|
|
assertEquals( "wrong 'current column' (1)", 0, gridControl.getCurrentColumn() );
|
|
assertEquals( "wrong 'current row' (1)", 0, gridControl.getCurrentRow() );
|
|
gridControl.goToCell( columnCount - 1, rowCount - 1 );
|
|
assertEquals( "wrong 'current column' (2)", dataModel.getColumnCount() - 1, gridControl.getCurrentColumn() );
|
|
assertEquals( "wrong 'current row' (2)", dataModel.getRowCount() - 1, gridControl.getCurrentRow() );
|
|
|
|
// removing the last column, while the active cell is in this very last column, is expected to adjust
|
|
// the active cell
|
|
columnModel.removeColumn( columnCount - 1 );
|
|
assertEquals( "removed the last and active column, active column was not adjusted!",
|
|
columnCount - 2, gridControl.getCurrentColumn() );
|
|
// same holds for rows
|
|
dataModel.removeRow( rowCount - 1 );
|
|
assertEquals( "removed the last and active row, active row was not adjusted!",
|
|
rowCount - 2, gridControl.getCurrentRow() );
|
|
|
|
impl_dispose(gridControl);
|
|
}
|
|
|
|
|
|
private XControl impl_createDialogWithGridControl() throws Exception
|
|
{
|
|
// create a simple dialog model/control/peer trinity
|
|
final XControlModel dialogModel = createInstance( XControlModel.class, "com.sun.star.awt.UnoControlDialogModel" );
|
|
m_disposables.add( dialogModel );
|
|
final XPropertySet dialogProps = UnoRuntime.queryInterface( XPropertySet.class, dialogModel );
|
|
dialogProps.setPropertyValue( "Width", 200 );
|
|
dialogProps.setPropertyValue( "Height", 100 );
|
|
dialogProps.setPropertyValue( "Title", "Grid Control Unit Test" );
|
|
final XControl dialogControl = createInstance( XControl.class, "com.sun.star.awt.UnoControlDialog" );
|
|
m_disposables.add( dialogControl );
|
|
dialogControl.setModel( dialogModel );
|
|
dialogControl.createPeer( createInstance( XToolkit.class, "com.sun.star.awt.Toolkit" ), null );
|
|
|
|
// insert a grid control model
|
|
final XMultiServiceFactory controlModelFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class,
|
|
dialogModel );
|
|
final XPropertySet gridModelProps = UnoRuntime.queryInterface( XPropertySet.class,
|
|
controlModelFactory.createInstance( "com.sun.star.awt.grid.UnoControlGridModel" ) );
|
|
m_disposables.add( gridModelProps );
|
|
gridModelProps.setPropertyValue( "PositionX", 6 );
|
|
gridModelProps.setPropertyValue( "PositionY", 6 );
|
|
gridModelProps.setPropertyValue( "Width", 188 );
|
|
gridModelProps.setPropertyValue( "Height", 88 );
|
|
final XNameContainer modelContainer = UnoRuntime.queryInterface( XNameContainer.class, dialogModel );
|
|
modelContainer.insertByName( "grid", gridModelProps );
|
|
|
|
// check the respective control has been created
|
|
final XControlContainer controlContainer = UnoRuntime.queryInterface( XControlContainer.class, dialogControl );
|
|
final XControl gridControl = controlContainer.getControl( "grid" );
|
|
assertNotNull( "no grid control created in the dialog", gridControl );
|
|
|
|
return gridControl;
|
|
}
|
|
|
|
|
|
private int impl_assertInteger( final Object i_object )
|
|
{
|
|
assertTrue( i_object instanceof Integer );
|
|
return ((Integer)i_object).intValue();
|
|
}
|
|
|
|
|
|
private void impl_assertColumnModelConsistency() throws IndexOutOfBoundsException
|
|
{
|
|
for ( int col = 0; col < m_columnModel.getColumnCount(); ++col )
|
|
{
|
|
final XGridColumn column = m_columnModel.getColumn( col );
|
|
assertNotNull( column );
|
|
assertEquals( "column/model inconsistency: column " + col + " has a wrong index!", col, column.getIndex() );
|
|
}
|
|
|
|
final XGridColumn[] allColumns = m_columnModel.getColumns();
|
|
assertEquals( "getColumns returns the wrong number of column objects",
|
|
m_columnModel.getColumnCount(), allColumns.length );
|
|
for ( int col = 0; col < m_columnModel.getColumnCount(); ++col )
|
|
{
|
|
assertTrue( "getColumns inconsistency", impl_areSameInterface( allColumns[col], m_columnModel.getColumn(col) ) );
|
|
}
|
|
}
|
|
|
|
|
|
private void impl_assertEquality( final XGridDataModel i_reference, final XGridDataModel i_compare ) throws IndexOutOfBoundsException
|
|
{
|
|
assertNotNull( i_reference );
|
|
assertNotNull( i_compare );
|
|
|
|
assertEquals( "data model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() );
|
|
assertEquals( "data model comparison: wrong row counts", i_reference.getRowCount(), i_compare.getRowCount() );
|
|
|
|
for ( int row = 0; row < i_reference.getRowCount(); ++row )
|
|
{
|
|
assertEquals( "data model comparison: wrong row heading content in row " + row,
|
|
i_reference.getRowHeading( row ) );
|
|
for ( int col = 0; col < i_reference.getRowCount(); ++col )
|
|
{
|
|
assertEquals( "data model comparison: wrong cell content in cell (" + col + ", " + row + ")",
|
|
i_reference.getCellData( col, row ) );
|
|
assertEquals( "data model comparison: wrong tooltip content in cell (" + col + ", " + row + ")",
|
|
i_reference.getCellToolTip( col, row ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void impl_assertEquality( final XGridColumnModel i_reference, final XGridColumnModel i_compare ) throws IndexOutOfBoundsException
|
|
{
|
|
assertEquals( "column model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() );
|
|
for ( int col = 0; col < i_reference.getColumnCount(); ++col )
|
|
{
|
|
final XGridColumn referenceColumn = i_reference.getColumn( col );
|
|
final XGridColumn compareColumn = i_compare.getColumn( col );
|
|
impl_assertEquality( referenceColumn, compareColumn );
|
|
}
|
|
}
|
|
|
|
|
|
private void impl_assertEquality( final XGridColumn i_reference, final XGridColumn i_compare )
|
|
{
|
|
final Method[] methods = XGridColumn.class.getMethods();
|
|
for ( int m=0; m<methods.length; ++m )
|
|
{
|
|
if ( !methods[m].getName().startsWith( "get" ) )
|
|
continue;
|
|
try
|
|
{
|
|
final Object referenceValue = methods[m].invoke( i_reference );
|
|
final Object compareValue = methods[m].invoke( i_compare );
|
|
assertEquals( "grid column comparison: column attribute '" + methods[m].getName().substring(3) + "' does not match",
|
|
referenceValue, compareValue );
|
|
}
|
|
catch ( java.lang.Exception ex )
|
|
{
|
|
fail( " could not retrieve object attributes: " + ex.toString() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private boolean impl_areSameInterface( final Object i_lhs, final Object i_rhs )
|
|
{
|
|
final XInterface lhs = UnoRuntime.queryInterface( XInterface.class, i_lhs );
|
|
final XInterface rhs = UnoRuntime.queryInterface( XInterface.class, i_rhs );
|
|
return UnoRuntime.areSame( lhs, rhs );
|
|
}
|
|
|
|
|
|
@Before
|
|
public void initTestCase()
|
|
{
|
|
m_disposables.clear();
|
|
}
|
|
|
|
|
|
@After
|
|
public void cleanupTestCase()
|
|
{
|
|
impl_dispose( m_disposables.toArray() );
|
|
}
|
|
|
|
|
|
@BeforeClass
|
|
public static void setUpConnection() throws java.lang.Exception
|
|
{
|
|
System.out.println( "--------------------------------------------------------------------------------" );
|
|
System.out.println( "starting class: " + GridControl.class.getName() );
|
|
System.out.print( "connecting ... " );
|
|
m_connection.setUp();
|
|
System.out.println( "done.");
|
|
|
|
final long seed = m_randomGenerator.nextLong();
|
|
m_randomGenerator.setSeed( seed );
|
|
System.out.println( "seeding random number generator with " + seed );
|
|
}
|
|
|
|
|
|
@AfterClass
|
|
public static void tearDownConnection()
|
|
throws InterruptedException, com.sun.star.uno.Exception
|
|
{
|
|
System.out.println();
|
|
System.out.println( "tearing down connection" );
|
|
m_connection.tearDown();
|
|
System.out.println( "finished class: " + GridControl.class.getName() );
|
|
System.out.println( "--------------------------------------------------------------------------------" );
|
|
}
|
|
|
|
|
|
public <T> T createInstance( Class<T> i_interfaceClass, final String i_serviceIndentifier ) throws Exception
|
|
{
|
|
return UnoRuntime.queryInterface( i_interfaceClass, createInstance( i_serviceIndentifier ) );
|
|
}
|
|
|
|
|
|
private Object createInstance( final String i_serviceName ) throws Exception
|
|
{
|
|
Object instance = m_context.getServiceManager().createInstanceWithContext( i_serviceName, m_context );
|
|
assertNotNull( "could not create an instance of '" + i_serviceName + "'", instance );
|
|
return instance;
|
|
}
|
|
|
|
private static final class DisposeListener implements XEventListener
|
|
{
|
|
DisposeListener( final Object i_component )
|
|
{
|
|
m_component = UnoRuntime.queryInterface( XComponent.class, i_component );
|
|
assertNotNull( m_component );
|
|
m_component.addEventListener( this );
|
|
}
|
|
|
|
public void disposing( EventObject i_event )
|
|
{
|
|
assertTrue( UnoRuntime.areSame( i_event.Source, m_component ) );
|
|
m_isDisposed = true;
|
|
}
|
|
|
|
final boolean isDisposed() { return m_isDisposed; }
|
|
|
|
private final XComponent m_component;
|
|
private boolean m_isDisposed;
|
|
}
|
|
|
|
|
|
private static final class ColumnModelListener implements XContainerListener
|
|
{
|
|
ColumnModelListener()
|
|
{
|
|
}
|
|
|
|
public void elementInserted( ContainerEvent i_event )
|
|
{
|
|
m_insertionEvents.add( i_event );
|
|
}
|
|
|
|
public void elementRemoved( ContainerEvent i_event )
|
|
{
|
|
m_removalEvents.add( i_event );
|
|
}
|
|
|
|
public void elementReplaced( ContainerEvent i_event )
|
|
{
|
|
m_replacementEvents.add( i_event );
|
|
}
|
|
|
|
public void disposing( EventObject eo )
|
|
{
|
|
}
|
|
|
|
private List< ContainerEvent > assertExclusiveInsertionEvents()
|
|
{
|
|
assertFalse( m_insertionEvents.isEmpty() );
|
|
assertTrue( m_removalEvents.isEmpty() );
|
|
assertTrue( m_replacementEvents.isEmpty() );
|
|
return m_insertionEvents;
|
|
}
|
|
|
|
private List< ContainerEvent > assertExclusiveRemovalEvents()
|
|
{
|
|
assertTrue( m_insertionEvents.isEmpty() );
|
|
assertFalse( m_removalEvents.isEmpty() );
|
|
assertTrue( m_replacementEvents.isEmpty() );
|
|
return m_removalEvents;
|
|
}
|
|
|
|
private void reset()
|
|
{
|
|
m_insertionEvents = new ArrayList< ContainerEvent >();
|
|
m_removalEvents = new ArrayList< ContainerEvent >();
|
|
m_replacementEvents = new ArrayList< ContainerEvent >();
|
|
}
|
|
|
|
private List< ContainerEvent > getInsertionEvents() { return m_insertionEvents; }
|
|
private List< ContainerEvent > getRemovalEvents() { return m_removalEvents; }
|
|
|
|
private List< ContainerEvent > m_insertionEvents = new ArrayList< ContainerEvent >();
|
|
private List< ContainerEvent > m_removalEvents = new ArrayList< ContainerEvent >();
|
|
private List< ContainerEvent > m_replacementEvents = new ArrayList< ContainerEvent >();
|
|
}
|
|
|
|
|
|
private static final OfficeConnection m_connection = new OfficeConnection();
|
|
private static Random m_randomGenerator = new Random();
|
|
private final XComponentContext m_context;
|
|
|
|
private XPropertySet m_gridControlModel;
|
|
private XGridColumnModel m_columnModel;
|
|
private XSortableMutableGridDataModel m_dataModel;
|
|
private final List< Object > m_disposables = new ArrayList< Object >();
|
|
}
|