Commit d8ad4b93 authored by Eric Tobias's avatar Eric Tobias

2.1.0 - Added the manager for spatial events and added listener calls in adapter.

+ Added sub-namespace "space"
+ Added SpatialEventType
+ Added listener list to abstract TuiAdapter as well as the related methods (add, remove, notify)
+ Added listener notify calls to concrete TuioAdapter
parent b669e054
......@@ -108,7 +108,7 @@ public abstract class TangibleApplication {
try {
adapter = (TuiAdapter) Class.forName(properties.getProperty("adapter.class")).getConstructor(TangibleInterfaceManager.class).newInstance(interfaceManager); //$NON-NLS-1$
} catch (InstantiationException e) {
logger.log(Level.SEVERE, "Cannot instantiate adapter. The adapter must be a concrete class, not an interface or abstract class and it must directly inherit from TuiAdapter! Check your properties file and set adapter.class accordingly."); //$NON-NLS-1$ s
logger.log(Level.SEVERE, "Cannot instantiate adapter. The adapter must be a concrete class, not an interface or abstract class and it must directly inherit from TuiAdapter! Check your properties file and set adapter.class accordingly."); //$NON-NLS-1$
System.exit(42);
} catch (IllegalAccessException e) {
logger.log(Level.SEVERE, "The currently executing method does not have access to the definition of the constructor."); //$NON-NLS-1$
......
......@@ -300,6 +300,15 @@ public class TangibleObject {
return ySpeed;
}
/**
* Method for retrieving this objects position data as a {@link Point}.
*
* @return The {@link Point} instance representing this objects location data.
*/
public Point getPosition() {
return new Point(this.getX(), this.getY(), this.getAngle(), Math.signum(this.rotationSpeed));
}
/**
* Simple setter method for the y velocity
*
......
......@@ -19,9 +19,13 @@ package lu.list.itis.dkd.tui.adapter;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.tui.TangibleInterfaceManager;
import lu.list.itis.dkd.tui.TangibleObjectManager;
import lu.list.itis.dkd.tui.event.SpatialEvent;
import lu.list.itis.dkd.tui.event.SpatialEventListener;
import lu.list.itis.dkd.tui.logging.EventLogger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -31,13 +35,15 @@ import javax.swing.JComponent;
* Abstract class managing interaction with a tangible table using a typical protocol.
*
* @author Nicolas GILMARD
* @author Eric TOBIAS [eric.tobias@list.lu]
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.0
* @version 2.0.2
* @version 2.1.0
*/
@NonNullByDefault
public abstract class TuiAdapter {
private List<SpatialEventListener> spatialEventListeners = new ArrayList<>();
/**
* An implementation of the {@link JComponent} handling all interactions with the visual
* interface as well as its construction.
......@@ -157,4 +163,28 @@ public abstract class TuiAdapter {
break;
}
}
/**
* Method used to add a {@link SpatialEventListener} to the list of listeners to notify.
*
* @param listener
* The listener to add.
*/
public synchronized void addListener(SpatialEventListener listener) {
spatialEventListeners.add(listener);
}
/**
* Method used to remove a {@link SpatialEventListener} from the list of listeners to notify.
*
* @param listener
* The listener to remove.
*/
public synchronized void removeListener(SpatialEventListener listener) {
spatialEventListeners.remove(listener);
}
protected synchronized void notifyListeners(SpatialEvent event) {
spatialEventListeners.forEach(listener -> listener.spaceUpdated(event));
}
}
\ No newline at end of file
......@@ -20,6 +20,8 @@ import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.tui.TangibleInterfaceManager;
import lu.list.itis.dkd.tui.TangibleObjectManager;
import lu.list.itis.dkd.tui.adapter.TangibleObject.Type;
import lu.list.itis.dkd.tui.event.SpatialEvent;
import lu.list.itis.dkd.tui.event.SpatialEvent.SpatialEventType;
import lu.list.itis.dkd.tui.logging.EventLogger;
import lu.list.itis.dkd.tui.utility.IdMapper;
import lu.list.itis.dkd.tui.utility.Point;
......@@ -312,7 +314,10 @@ public class TuioAdapter extends TuiAdapter implements TuioListener {
private synchronized void addTangibleObject(TangibleObject object) {
tangibleObjectList.put(object.getObjectId(), object);
logInfo(object, "down"); //$NON-NLS-1$
objectManager.dropObject(object);
synchronized (this) {
objectManager.dropObject(object);
notifyListeners(new SpatialEvent(object, object.getPosition(), SpatialEventType.INSERT));
}
}
/**
......@@ -373,7 +378,10 @@ public class TuioAdapter extends TuiAdapter implements TuioListener {
private synchronized void removeTangibleObject(TangibleObject object) {
tangibleObjectList.remove(object.getObjectId(), object);
logInfo(object, "up"); //$NON-NLS-1$
objectManager.liftObject(object);
synchronized (this) {
objectManager.liftObject(object);
notifyListeners(new SpatialEvent(object, object.getPosition(), SpatialEventType.REMOVE));
}
}
/**
......@@ -424,7 +432,10 @@ public class TuioAdapter extends TuiAdapter implements TuioListener {
*/
private synchronized void updateTangibleObject(TangibleObject object) {
logInfo(object, "move"); //$NON-NLS-1$
objectManager.updateObject(object);
synchronized (this) {
objectManager.updateObject(object);
notifyListeners(new SpatialEvent(object, object.getPosition(), SpatialEventType.UPDATE));
}
}
/**
......
......@@ -44,6 +44,7 @@ public class SpatialEvent extends EventObject {
private static final long serialVersionUID = 7414656845719882078L;
protected Point location;
protected SpatialEventType type;
/**
* Constructor initializing all fields.
......@@ -54,14 +55,17 @@ public class SpatialEvent extends EventObject {
* @param location
* The location the event was triggered on. Note that the location must be expressed in
* {@link ScreenCoordinates}.
* @param type
* The type of the event that is to be triggered.
* @ore location.getState().getClass() == ScreenCoordinates.class
*/
public SpatialEvent(TangibleObject source, Point location) {
public SpatialEvent(TangibleObject source, Point location, SpatialEventType type) {
super(source);
Preconditions.checkArgument(location.getState().getClass().equals(ScreenCoordinates.class), "The location must be given in screen coordinates!"); //$NON-NLS-1$
this.location = location;
this.type = type;
}
/**
......@@ -72,4 +76,46 @@ public class SpatialEvent extends EventObject {
public Point getLocation() {
return location;
}
/**
* Simple getter method for type.
*
* @return The value of type.
*/
public SpatialEventType getType() {
return type;
}
/**
* Method for returning a {@link TangibleObject}, the source of this event. The method was
* overridden to avoid casting.
*/
@Override
public TangibleObject getSource() {
return (TangibleObject) source;
}
/**
* Enumeration holding all possible types for {@link SpatialEvent} instances.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.0
*/
public enum SpatialEventType {
/**
* The event type used when an event is triggered due to a new insertion into the tracked
* space.
*/
INSERT,
/**
* The event type used when an event is triggered by an update of an already tracked object.
*/
UPDATE,
/**
* The event type used when an event is triggered due to a removal from an object from the
* tracked space.
*/
REMOVE;
}
}
\ 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.space;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.utility.ScreenCoordinates;
import java.util.Comparator;
/**
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.0
*/
@NonNullByDefault
public class HorizontalPositionComparator implements Comparator<TangibleObject> {
/**
* Method used to compare two {@link TangibleObject} instances. The method will return 0 if both
* instances are equal. None of the provided parameters may be <code>null</code>.
*
* The method will use {@link Double#compare(double, double)} on the x-axis position of the
* provided {@link TangibleObject} instances after retrieving and converting their points to
* {@link ScreenCoordinates}.
*/
@Override
public int compare(TangibleObject thisTangible, TangibleObject thatTangible) {
if (thisTangible.equals(thatTangible)) {
return 0;
}
return Double.compare(thisTangible.getPosition().toScreenCoordinates().getX(), thatTangible.getPosition().toScreenCoordinates().getX());
}
}
\ 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.space;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.event.SpatialEvent;
import lu.list.itis.dkd.tui.event.SpatialEventListener;
import lu.list.itis.dkd.tui.utility.PropertiesFetcher;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.util.Properties;
import java.util.TreeSet;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Singleton class managing the relative positioning of all tangible objects. The manger listens to
* {@link SpatialEvent}s and interprets the the latter to construct a position map of all tracked
* tangible objects.
*
* To simplify matters, the manager currently only tracks relative horizontal and vertical
* positioning. Therefore, it can be queried for objects to the left, right, above, or below a given
* object but won't be able to give information about objects on other axis.
*
* By design, the objects are tracked by ID. This means that objects are differentiated by their
* unique identifier. Hence, it is important that objects are updated as they are manipulated.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.0
*/
public class SpatialPositioningManager implements SpatialEventListener {
private static final SpatialPositioningManager INSTANCE = new SpatialPositioningManager();
private static final Logger logger = Logger.getLogger(SpatialPositioningManager.class.getSimpleName());
private static final Properties properties = PropertiesFetcher.fetchProperties();
private TreeSet<TangibleObject> horizontalOrder = new TreeSet<>(new HorizontalPositionComparator());
private TreeSet<TangibleObject> verticalOrder = new TreeSet<>(new VerticalPositionComparator());
/**
* Constructor initializing all fields.
*/
private SpatialPositioningManager() {
// TODO Auto-generated constructor stub
}
/**
* Method used to return the only instance of the {@link SpatialPositioningManager}.
*
* @return The only valid instance of this {@link SpatialPositioningManager}.
*/
public SpatialPositioningManager getInstance() {
return INSTANCE;
}
/**
* Method for configuring a {@link Logger} with several properties as given by the loader
* properties file.
*
* @param unconfiguredLogger
* The {@link Logger} instance to configure.
* @pre logger != null
*/
public static void configureLogger(final Logger unconfiguredLogger) {
Preconditions.checkArgument(unconfiguredLogger != null, "The provider logger must not be null!"); //$NON-NLS-1$
if (Boolean.parseBoolean(properties.getProperty("logger.event.output.enabled"))) { //$NON-NLS-1$
try {
unconfiguredLogger.addHandler(new FileHandler(properties.getProperty("logger.event.output.location"), true)); //$NON-NLS-1$
} catch (IOException e) {
unconfiguredLogger.log(Level.SEVERE, "The logger could not be configured!", e); //$NON-NLS-1$
}
}
unconfiguredLogger.setLevel(Level.parse(properties.getProperty("logger.level"))); //$NON-NLS-1$
}
/** {@inheritDoc} */
@Override
public void spaceUpdated(SpatialEvent event) {
switch (event.getType()) {
case UPDATE:
horizontalOrder.remove(event.getSource());
verticalOrder.remove(event.getSource());
// The update call would need to remove and then reinsert the objects to trigger a
// rearrangement.
// $FALL-THROUGH$
case INSERT:
horizontalOrder.add(event.getSource());
verticalOrder.add(event.getSource());
break;
case REMOVE:
horizontalOrder.remove(event.getSource());
verticalOrder.remove(event.getSource());
break;
default:
logger.log(Level.SEVERE, "Spatial event type \"" + event.getType() + "\" not recognized!"); //$NON-NLS-1$ //$NON-NLS-2$
break;
}
}
}
\ 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.space;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.utility.ScreenCoordinates;
import java.util.Comparator;
/**
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.0
*/
public class VerticalPositionComparator implements Comparator<TangibleObject> {
/**
* Method used to compare two {@link TangibleObject} instances. The method will return 0 if both
* instances are equal. None of the provided parameters may be <code>null</code>.
*
* The method will use {@link Double#compare(double, double)} on the y-axis position of the
* provided {@link TangibleObject} instances after retrieving and converting their points to
* {@link ScreenCoordinates}.
*
* <b>Important</b>: the provided coordinate system for the screen uses an inverse y-axis.
*/
@Override
public int compare(TangibleObject thisTangible, TangibleObject thatTangible) {
if (thisTangible.equals(thatTangible)) {
return 0;
}
return Double.compare(thisTangible.getPosition().toScreenCoordinates().getY(), thatTangible.getPosition().toScreenCoordinates().getY());
}
}
\ No newline at end of file
/**
* Copyright Luxembourg Institute of Science and Technology (LIST), 2015. All rights reserved. If
* you wish to use this code for any purpose, please contact the author.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lu.list.itis.dkd.tui.utility;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Helper class used to fetch properties for different classes or occasions.
*
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.1
* @version 2.1.0
*/
public final class PropertiesFetcher {
private static final String TANGIBLE_GIS_PROPERTIES = "properties.properties"; //$NON-NLS-1$
private static Properties properties;
private static Logger logger = Logger.getLogger(PropertiesFetcher.class.getSimpleName());
/** Private constructor to avoid instantiation. */
private PropertiesFetcher() {}
/**
* Method loading the root property file. The properties must be named
* <code>propeties.properties</code> and located on the class path accessible by the the class
* loader to find. The method will return a previously loaded property file if it exists.
*
* @return The root property file.
*/
public static Properties fetchProperties() {
if (properties != null) {
return properties;
}
properties = new Properties();
try {
loadFromExternalFile();
} catch (final IOException e) {
logger.log(Level.INFO, "Proeprties file could not be loaded from external location!"); //$NON-NLS-1$
try {
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(PropertiesFetcher.TANGIBLE_GIS_PROPERTIES));
} catch (final IOException ioe) {
logger.log(Level.SEVERE, "The porperties file could not be loaded!", ioe); //$NON-NLS-1$
}
}
return properties;
}
/**
* Method used to load the properties file from an external location such that it can be easily
* modified. The file needs to reside along the JAR launching this program.
*
* @throws IOException
* Thrown when the file could not be located or loaded.
*/
private static void loadFromExternalFile() throws IOException {
try (FileInputStream inputStream = new FileInputStream(new File(TANGIBLE_GIS_PROPERTIES))) {
properties.load(inputStream);
}
}
/**
* Method loading a given property file. The properties must be on the class path accessible by
* the the class loader to find.
*
* @param location
* The name of the properties file to load.
* @return The property file.
*/
public static Properties fetchProperties(final String location) {
Properties userDefinedProperties = new Properties();
try {
userDefinedProperties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(location));
} catch (final IOException e) {
Logger.getLogger(PropertiesFetcher.class.getSimpleName()).log(Level.SEVERE, "The porperties file could not be loaded!", e); //$NON-NLS-1$
}
return userDefinedProperties;
}
}
\ No newline at end of file
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