Commit 7e27f9b6 authored by Eric Tobias's avatar Eric Tobias

2.1.4 - Made SpatialMatrix a Singleton and added a return to the update call.

+ A call to update will now return whether the update changed the underlying data structure
+ Added PositionChangeListener
+ Called above listener when the underlying data structures are changed on a spatial event.
+ Made SpatialMatrix a singleton to better reflect its nature as a class maintaining some of the application state.
+ Added various test classes.
parent 5ff4cc20
2.1.4
+ Added test method for various classes.
+ Changed update method to return whether the underlying data structure have indeed been changed.
+ Added PositionChangeListener.
2.1.3
+ Added method to retrieve widgets aligned with one another horizontally and vertically.
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2015. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.event;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import java.util.EventObject;
/**
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.4
*/
public class PositionChangeEvent extends EventObject {
private static final long serialVersionUID = -7146965508061315693L;
/**
* The source that triggered the event.
*
* @param source
*/
public PositionChangeEvent(TangibleObject source) {
super(source);
}
}
\ No newline at end of file
/**
* Copyright Luxembourg Institute of Science and Technology, 2015. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.event;
import lu.list.itis.dkd.tui.space.SpatialPositioningManager;
import java.util.EventListener;
/**
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.4
*/
public interface PositionChangeListener extends EventListener {
/**
* Method invoked when a position tracked by the spatial position manager has been changed.
*
* @param manager
* The manager holding information on the spatial layout.
*/
public void positionChanged(SpatialPositioningManager manager);
}
\ No newline at end of file
......@@ -36,6 +36,18 @@ public class SpatialMatrix {
private ArrayList<TangibleObject> horizontal = new ArrayList<>();
private ArrayList<TangibleObject> vertical = new ArrayList<>();
private static final SpatialMatrix INSTANCE = new SpatialMatrix();
private SpatialMatrix() {}
/**
* Singleton getter.
*
* @return The unique, single instance of this class.
*/
public static SpatialMatrix getInstance() {
return INSTANCE;
}
/**
* Method used to add an element to the matrix. The method will insert the element into the
......@@ -74,10 +86,20 @@ public class SpatialMatrix {
*
* @param object
* The object to update.
* @return Will return <code>true</code> if the underlying horizontal or vertical lists have
* been changed, that is, if the order of any of those lists have been changed. The
* method will use an equality check on the lists before (using a copy) and after the
* operations. The method will return <code>false</code> if both lists are equal.
* @see ArrayList#equals(Object)
*/
public synchronized void update(TangibleObject object) {
public synchronized boolean update(TangibleObject object) {
ArrayList<TangibleObject> horizontalCopy = new ArrayList<>(horizontal);
ArrayList<TangibleObject> verticalCopy = new ArrayList<>(vertical);
remove(object);
add(object);
return !(horizontalCopy.equals(horizontal) && verticalCopy.equals(vertical));
}
......@@ -393,4 +415,12 @@ public class SpatialMatrix {
return "Vertical : " + verticalBuilder.toString() + "\n" + "Horizontal : " + horizontalbuilder.toString(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Method used to clear the internal maps held by the matrix. Useful during unit testing.
*/
public void clear() {
horizontal.clear();
vertical.clear();
}
}
\ No newline at end of file
......@@ -20,6 +20,7 @@ import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.TangibleObjectManager;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.event.PositionChangeListener;
import lu.list.itis.dkd.tui.event.SpatialEvent;
import lu.list.itis.dkd.tui.event.SpatialEventListener;
import lu.list.itis.dkd.tui.utility.Point;
......@@ -63,7 +64,9 @@ public class SpatialPositioningManager implements SpatialEventListener {
private static final Properties properties = PropertiesFetcher.fetchProperties();
private static final SpatialPositioningManager INSTANCE = new SpatialPositioningManager();
private SpatialMatrix spatialMatrix = new SpatialMatrix();
private List<PositionChangeListener> positionChangeListeners = new ArrayList<>();
private SpatialMatrix spatialMatrix = SpatialMatrix.getInstance();
/**
* Enumeration showing all alignment types this class supports.
......@@ -127,7 +130,9 @@ public class SpatialPositioningManager implements SpatialEventListener {
}
/**
* {@inheritDoc} <br/>
* {@inheritDoc} The method will also notify all {@link PositionChangeListener} instances of any
* change, that is, it will notify all of any insertion or removals but only notify on updates
* if there is indeed a change to the underlying spatial matrix.<br/>
* <br/>
*
* The event will not be registered if the if of the tangible is not recognized by the
......@@ -142,13 +147,17 @@ public class SpatialPositioningManager implements SpatialEventListener {
switch (event.getType()) {
case UPDATE:
spatialMatrix.update(event.getSource());
if (spatialMatrix.update(event.getSource())) {
notifyPositionChangeListeners(this);
}
break;
case INSERT:
spatialMatrix.add(event.getSource());
notifyPositionChangeListeners(this);
break;
case REMOVE:
spatialMatrix.remove(event.getSource());
notifyPositionChangeListeners(this);
break;
default:
logger.log(Level.SEVERE, "Spatial event type \"" + event.getType() + "\" not recognized!"); //$NON-NLS-1$ //$NON-NLS-2$
......@@ -169,6 +178,26 @@ public class SpatialPositioningManager implements SpatialEventListener {
// // //$NON-NLS-2$
}
/**
* Method used to add a position change listener to this instance of the manager.
*
* @param listener
* The listener to add and notify of any changes.
*/
public void addPositionChangeListener(PositionChangeListener listener) {
this.positionChangeListeners.add(listener);
}
/**
* Method invoked to notify all {@link PositionChangeListener} instances of a change.
*
* @param manager
* The manager holding information on the spatial layout.
*/
public void notifyPositionChangeListeners(SpatialPositioningManager manager) {
positionChangeListeners.forEach(listener -> listener.positionChanged(manager));
}
/**
* The method will return the left-most widget, that is, the first widget found based based on
* its x-axis coordinate. The retrieval is based on the underlying handles.
......@@ -418,6 +447,6 @@ public class SpatialPositioningManager implements SpatialEventListener {
* testing.
*/
public void reinitialize() {
spatialMatrix = new SpatialMatrix();
spatialMatrix.clear();
}
}
\ No newline at end of file
/**
* Copyright Luxembourg Institute of Science and Technology, 2015. All rights reserved.
*
* This file is part of TULIP.
*
* TULIP is free software: you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation, version 3 of the
* License.
*
* TULIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.event;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.tui.TangibleApplication;
import lu.list.itis.dkd.tui.TangibleInterfaceManager;
import lu.list.itis.dkd.tui.TangibleObjectManager;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.adapter.TangibleObject.Type;
import lu.list.itis.dkd.tui.adapter.TangibleObjectBuilder;
import lu.list.itis.dkd.tui.adapter.TuiAdapter;
import lu.list.itis.dkd.tui.event.SpatialEvent.SpatialEventType;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.space.SpatialPositioningManager;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.widget.BaseWidget;
import lu.list.itis.dkd.tui.widget.builder.BaseWidgetBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Properties;
import java.util.logging.Logger;
/**
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.4
*/
public class PositionChangeListenerTest {
/**
* Mock for the {@link PositionChangeListener} registering whether it was called and the
* parameter passed to it.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.4
*/
public static class PositionChangeListenerMock implements PositionChangeListener {
private boolean called;
private SpatialPositioningManager caller;
/**
* Simple getter method for called.
*
* @return The value of called.
*/
public boolean wasCalled() {
return called;
}
/**
* Simple getter method for caller.
*
* @return The value of caller.
*/
public SpatialPositioningManager getCaller() {
return caller;
}
/** {@inheritDoc} */
@Override
public void positionChanged(SpatialPositioningManager manager) {
called = true;
caller = manager;
}
/**
* Method used to reset the values stored by the mock listener.
*/
public void reset() {
called = false;
caller = null;
}
}
/**
* Class implementing the abstract {@link TangibleApplication} for test purposes.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.3
*/
public class TestTangibleApplication extends TangibleApplication {
/**
* Constructor initialising several basic properties.
*
* @param newPropertiesFileURI
* The URI of the properties file to load.
* @throws Exception
*/
protected TestTangibleApplication(String newPropertiesFileURI) throws Exception {
super(newPropertiesFileURI);
objectManager = new TestTangibleObjectManager(this, TangibleApplication.logger, properties);
objectManager.defineWidgets();
this.adapter.setObjectManager(objectManager);
this.adapter.addListener(SpatialPositioningManager.getInstance());
}
@SuppressWarnings("javadoc")
public TuiAdapter getAdapter() {
return this.adapter;
}
}
/**
* Class implementing the abstract {@link TangibleApplication} for test purposes.
*
* @author Eric Tobias [eric.tobias@tudor.lu]
* @since 2.1
* @version 2.1.3
*/
@SuppressWarnings("serial")
public class TestTangibleInterfaceManager extends TangibleInterfaceManager {
/**
* @see TangibleInterfaceManager
*/
@SuppressWarnings("javadoc")
public TestTangibleInterfaceManager(TangibleApplication applicationContext, Logger applicationLogger, Properties properties) {
super(applicationContext, applicationLogger, properties);
}
}
/**
* Class implementing the abstract {@link TangibleObjectManager} for test purposes.
*
* @author Eric Tobias [eric.tobias@tudor.lu]
* @since 2.1
* @version 2.1.3
*/
public class TestTangibleObjectManager extends TangibleObjectManager {
/**
* Constructor initialising several fields and setting up the list of managed tangibles.
*
* @param applicationContext
* The {@link TangibleApplication} hosting this manager.
* @param applicationLogger
* The {@link Logger} to use for all logging purposes.
* @param properties
* The properties instance from which to load the default values from.
*/
@NonNullByDefault
public TestTangibleObjectManager(TangibleApplication applicationContext, Logger applicationLogger, Properties properties) {
super(applicationContext, applicationLogger, properties);
}
/**
* {@inheritDoc}
*/
@Override
public void defineWidgets() throws BuildException {
BaseWidget baseWidget = new BaseWidgetBuilder()
.withHandle(0, new Point(0, 0, 0))
.build();
objectList.put(0, baseWidget);
BaseWidget baseWidget_1 = new BaseWidgetBuilder()
.withHandle(1, new Point(10, 10, 10))
.build();
objectList.put(1, baseWidget_1);
BaseWidget baseWidget_2 = new BaseWidgetBuilder()
.withHandle(2, new Point(20, 20, 20))
.build();
objectList.put(2, baseWidget_2);
BaseWidget baseWidget_3 = new BaseWidgetBuilder()
.withHandle(3, new Point(30, 30, 30))
.build();
objectList.put(3, baseWidget_3);
BaseWidget baseWidget_4 = new BaseWidgetBuilder()
.withHandle(4, new Point(40, 40, 40))
.build();
objectList.put(4, baseWidget_4);
}
}
private TestTangibleApplication application;
private static TangibleObjectBuilder builderZero;
private static TangibleObjectBuilder builderOne;
private static TangibleObjectBuilder builderTwo;
private static TangibleObjectBuilder builderThree;
private static TangibleObjectBuilder builderFour;
private TangibleObject objectZero;
private TangibleObject objectOne;
private TangibleObject objectTwo;
private TangibleObject objectThree;
private TangibleObject objectFour;
private static PositionChangeListenerMock listenerMock = new PositionChangeListenerMock();
/**
* @throws Exception
*
*/
@BeforeClass
public static void setUpBefore() throws Exception {
builderZero = new TangibleObjectBuilder(0, Type.OBJECT, 0, 0);
builderOne = new TangibleObjectBuilder(1, Type.CURSOR, 10, 10);
builderTwo = new TangibleObjectBuilder(2, Type.BLOB, 20, 20);
builderThree = new TangibleObjectBuilder(3, Type.OBJECT, 30, 30);
builderFour = new TangibleObjectBuilder(4, Type.CURSOR, 40, 40);
SpatialPositioningManager.getInstance().addPositionChangeListener(listenerMock);
}
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
application = new TestTangibleApplication("test_properties.properties"); //$NON-NLS-1$
objectZero = builderZero.build();
objectOne = builderOne.build();
objectTwo = builderTwo.build();
objectThree = builderThree.build();
objectFour = builderFour.build();
}
/**
* Method used to reinitialize the spatial matrix after each test.
*/
@After
public void after() {
SpatialPositioningManager.getInstance().reinitialize();
objectOne.getPosition().setLocation(new Point(10, 10, 10));
}
/**
* Method used for testing whether the {@link PositionChangeListener} works as intended.
* Incidentally this also tests whether updates on positions are sent out as expected.
*/
@Test
public void test() {
assertFalse(listenerMock.wasCalled());
application.getAdapter().notifyListeners(new SpatialEvent(objectOne, objectOne.getPosition(), SpatialEventType.INSERT));
assertTrue(listenerMock.wasCalled());
assertEquals(SpatialPositioningManager.getInstance(), listenerMock.getCaller());
listenerMock.reset();
application.getAdapter().notifyListeners(new SpatialEvent(objectZero, objectZero.getPosition(), SpatialEventType.INSERT));
application.getAdapter().notifyListeners(new SpatialEvent(objectThree, objectThree.getPosition(), SpatialEventType.INSERT));
application.getAdapter().notifyListeners(new SpatialEvent(objectTwo, objectTwo.getPosition(), SpatialEventType.INSERT));
application.getAdapter().notifyListeners(new SpatialEvent(objectFour, objectFour.getPosition(), SpatialEventType.INSERT));
listenerMock.reset();
assertFalse(listenerMock.wasCalled());
application.getAdapter().notifyListeners(new SpatialEvent(objectFour, objectFour.getPosition(), SpatialEventType.REMOVE));
assertTrue(listenerMock.wasCalled());
assertEquals(SpatialPositioningManager.getInstance(), listenerMock.getCaller());
listenerMock.reset();
objectOne.getPosition().setLocation(new Point(25, 25, 25));
application.getAdapter().notifyListeners(new SpatialEvent(objectOne, objectOne.getPosition(), SpatialEventType.UPDATE));
assertTrue(listenerMock.wasCalled());
assertEquals(SpatialPositioningManager.getInstance(), listenerMock.getCaller());
}
}
\ No newline at end of file
......@@ -172,7 +172,6 @@ public class SpatialMatrixTest {
/**
* @throws Exception
*
*/
@BeforeClass
public static void setUpBefore() throws Exception {
......@@ -181,7 +180,6 @@ public class SpatialMatrixTest {
builderTwo = new TangibleObjectBuilder(2, Type.BLOB, 20, 20);
builderThree = new TangibleObjectBuilder(3, Type.OBJECT, 30, 30);
builderFour = new TangibleObjectBuilder(4, Type.CURSOR, 40, 40);
}
/**
......@@ -205,6 +203,12 @@ public class SpatialMatrixTest {
@After
public void after() {
SpatialPositioningManager.getInstance().reinitialize();
objectZero.getPosition().toCameraCoordinates().setLocation(new Point(0, 0, 0));
objectOne.getPosition().toCameraCoordinates().setLocation(new Point(10, 10, 10));
objectTwo.getPosition().toCameraCoordinates().setLocation(new Point(20, 20, 20));
objectThree.getPosition().toCameraCoordinates().setLocation(new Point(30, 30, 30));
objectFour.getPosition().toCameraCoordinates().setLocation(new Point(40, 40, 40));
}
/**
......@@ -301,6 +305,31 @@ public class SpatialMatrixTest {
assertEquals(5, matrix.getHorizontal().size());
}
/**
* Test method for
* {@link lu.list.itis.dkd.tui.space.SpatialMatrix#update(lu.list.itis.dkd.tui.adapter.TangibleObject)}
* , specifically, for testing its return value.
*/
@Test
public void voidTestUpdateReturn() {
application.getAdapter().notifyListeners(new SpatialEvent(objectZero, objectZero.getPosition(), SpatialEventType.INSERT));
application.getAdapter().notifyListeners(new SpatialEvent(objectOne, objectOne.getPosition(), SpatialEventType.INSERT));
application.getAdapter().notifyListeners(new SpatialEvent(objectThree, objectThree.getPosition(), SpatialEventType.INSERT));
application.getAdapter().notifyListeners(new SpatialEvent(objectTwo, objectTwo.getPosition(), SpatialEventType.INSERT));
application.getAdapter().notifyListeners(new SpatialEvent(objectFour, objectFour.getPosition(), SpatialEventType.INSERT));
assertFalse(matrix.update(objectOne));
objectOne.getPosition().toCameraCoordinates().setLocation(new Point(1, 1, 1));
assertFalse(matrix.update(objectOne));
objectThree.getPosition().toCameraCoordinates().setLocation(new Point(45, 45, 45));
assertTrue(matrix.update(objectThree));
objectThree.getPosition().toCameraCoordinates().setLocation(new Point(5, 5, 5));
assertTrue(matrix.update(objectThree));
}
/**
* Test method for {@link lu.list.itis.dkd.tui.space.SpatialMatrix#leftOf(int)}.
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment