Dear users, Please note that, from Monday, August 16, 2019, RSA keys shorter than 2048bit will no longer be accepted for security reasons. Please update your keys as needed before this date. If you need assistance with regard to this process, please contact sia@list.lu

Thank you for your understanding.

Commit a8af8a3b authored by Nico Mack's avatar Nico Mack

Implementation of connectable feature

parent a7a72941
......@@ -32,9 +32,16 @@ COLOURSC_NODE=colourscheme
CONDITION_NODE=condition
CONDITIONS_NODE=conditions
CONDITION_BUILDER_NAMESPACE=lu.list.itis.dkd.tui.event.conditional.builder
CONNECTABLE_NODE=connectable
CONNECTION_BUILDER_NAMESPACE=lu.list.itis.dkd.tui.feature.connection.builder
CONNECTIONS_NODE=connections
CONNECTION_NODE=connection
CONNECTORS_NODE=connectors
CONNECTOR_NODE=connector
CONTENT_NODE=content
CONTENT_BUILDER_NAMESPACE=lu.list.itis.dkd.tui.content.builder
CONTROL_POINTS_NODE=controlPoints
CONTROL_POINT_NODE=controlPoint
CORNER_RADIUS_NODE=cornerRadius
CORONA_BUILDER_NAMESPACE=lu.list.itis.dkd.tui.widget.corona.builder
CORONA_NODE=corona
......@@ -64,6 +71,7 @@ EFFECTIVITY_THRESHOLD_NODE=effectivityThreshold
EMPTY_STRING=
END_NODE=end
ENDING_NODE=ending
ENDING_OFFSET_NODE=endingOffset
ENERGY_THRESHOLD_NODE=energyThreshold
EXCLUSIVE_NODE=exclusive
EXPRESSION_NODE=expression
......@@ -102,6 +110,7 @@ INNER_RADIUS_NODE=innerRadius
INTERACTIONS_NODE=interactions
INTERACTION_NODE=interaction
INTERACTION_BUILDER_NAMESPACE=lu.list.itis.dkd.tui.event.interaction.builder
INTERPOLATION_STEPS_NODE=interpolationSteps
INSET_BORDER_NODE=insetBorder
JAVA_AWT_COLOR_NAMESPACE=java.awt.Color
LABEL_COLOUR_ELEMENT=labelColour
......
......@@ -24,6 +24,7 @@ import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.feature.connection.Connection;
import lu.list.itis.dkd.tui.utility.IdMapper;
import lu.list.itis.dkd.tui.utility.StringUtils;
import lu.list.itis.dkd.tui.widget.BaseWidget;
......@@ -61,6 +62,7 @@ import java.util.concurrent.ConcurrentHashMap;
@NonNullByDefault
public abstract class TangibleObjectManager {
protected static volatile ConcurrentHashMap<String, Connection> connectionMap = new ConcurrentHashMap<>();
/**
* This dictionary stores all tangibles detected by reacTIVision relevant to the application.
*/
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2016.
*
* This file is part of TULIP.
*
* TULIP is licensed under a dual-licensing scheme. For non-commercial purposes, the LGPL version 3,
* as stated below, is applicable. For all commercial purposes TULIP is licensed under a LIST
* proprietary license. Please contact LIST at tto@list.lu to obtain a commercial license.
*
* For all non-commercial purposes, 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.bootstrapping;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.feature.connection.Connection;
import lu.list.itis.dkd.tui.feature.connection.builder.BaseConnectionBuilder;
import lu.list.itis.dkd.tui.utility.Externalization;
import org.jdom2.Element;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author Eric Tobias [eric.tobias@list.lu]
* @since 2.2
* @version 2.3.0
*/
public class ConnectionBootstrapper {
/**
* Method used to determine the appropriate builder for a given object and then issue a build call.
*
* @param bootstrapContext
* @param callback
*
* @param tetherNode
* The node from a larger document that contains, as children, all the necessary information
* to resolve the correct builder and build the final tether.
* @return The final tether as defined by the children of the element node.
* @throws ClassNotFoundException
* Thrown when the class of the builder for the widget or those of the nested corona
* builder(s) could not be found.
* @throws SecurityException
* Thrown when the constructor cannot be retrieved due to some security constraints.
* @throws NoSuchMethodException
* Thrown when no constructor with the given parameter type is available.
* @throws InvocationTargetException
* Thrown if the invocation of any constructor through reflection throws an exception.
* @throws IllegalArgumentException
* Thrown when the provided argument is not eligible for the builder's constructor.
* @throws IllegalAccessException
* Thrown if this Constructor object is enforcing Java language access control and the
* underlying constructor is inaccessible.
* @throws InstantiationException
* Thrown if the class that declares the underlying constructor represents an abstract
* class.
* @throws BuildException
*/
@SuppressWarnings("unchecked")
public static Connection buildConnectionFromElement(Element connectionNode, BootstrapContext bootstrapContext, BootstrapCallback callback) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, BuildException {
Connection instance = null;
Element type = connectionNode.getChild(Externalization.TYPE_NODE);
Class<?> builder = Class.forName(Externalization.CONNECTION_BUILDER_NAMESPACE + Externalization.NAMESPACE_SEPARATOR + type.getValue() + Externalization.BUILDER_CLASS_POSTFIX);
if ((bootstrapContext == null) || (bootstrapContext.size() == 0)) {
Constructor<BaseConnectionBuilder<?>> constructor = (Constructor<BaseConnectionBuilder<?>>) builder.getConstructor(new Class[] {Element.class});
instance = constructor.newInstance(new Object[] {connectionNode}).build();
} else {
Constructor<BaseConnectionBuilder<?>> constructor = (Constructor<BaseConnectionBuilder<?>>) builder.getConstructor(new Class[] {Element.class, BootstrapContext.class, BootstrapCallback.class});
instance = constructor.newInstance(new Object[] {connectionNode, bootstrapContext, callback}).build();
}
return instance;
}
}
\ No newline at end of file
......@@ -24,6 +24,7 @@ import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.tui.TangibleApplication;
import lu.list.itis.dkd.tui.TangibleObjectManager;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.feature.connection.Connection;
import lu.list.itis.dkd.tui.utility.Externalization;
import lu.list.itis.dkd.tui.utility.StringUtils;
import lu.list.itis.dkd.tui.utility.Templating;
......@@ -159,6 +160,8 @@ public class TangibleObjectBootstrapper extends TangibleObjectManager {
for (Element connectionNode : connectionsNode.getChildren()) {
if (Externalization.TETHERS_NODE.equals(connectionNode.getName())) {
this.loadTethers(connectionNode);
} else if (Externalization.CONNECTORS_NODE.equals(connectionNode.getName())) {
this.loadConnectors(connectionNode);
} else {
throw new BuildException(StringUtils.build("Unrecognized connection type {} !", connectionsNode.getName())); //$NON-NLS-1$
}
......@@ -194,6 +197,23 @@ public class TangibleObjectBootstrapper extends TangibleObjectManager {
}
}
private void loadConnectors(Element connectorsRootNode) throws BuildException {
List<Element> connectorNodes = connectorsRootNode.getChildren(Externalization.CONNECTOR_NODE);
try {
for (Element connectorNode : connectorNodes) {
Connection connection = ConnectionBootstrapper.buildConnectionFromElement(connectorNode, null, null);
if (connectionMap.put(connection.getName(), connection) != null) {
LOGGER.warn("Multiple instances of connection with name {} exist!", connection.getName()); //$NON-NLS-1$
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) {
LOGGER.error("One of the desired connections could not be build!", e); //$NON-NLS-1$
connectionMap.clear();
throw new BuildException("One of the desired connections could not be build!", e); //$NON-NLS-1$
}
}
/**
* builds a cursor from the specified template node. A second parameter allows specifying the
* bootstrapping context, a map holding the names and the values of variables for interpolation of
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2019. 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.feature.connection;
import lu.list.itis.dkd.tui.utility.Point;
import java.util.List;
/**
* @author nico.mack@list.lu
* @since 2.6
* @version 1.0.0
* @param <T>
*/
public interface Connectable<T> extends Comparable<T> {
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public enum End {
ORIGIN, ENDING
}
/**
* @return
*/
public Point getConnector();
/**
* @return
*/
public List<Connection> getConnections();
/**
* @return
*/
public boolean isConnected();
/**
* @return
*/
public boolean isExclusive();
/**
* @return
*/
public int getNumberOfConnections();
/**
* @param candidate
* @return
*/
public boolean canConnect(Connectable<?> candidate);
/**
* @param connector
* @return
*/
public boolean connect(Connectable<?> candidate, Connection connection, End end);
/**
* @param connector
* @return
*/
public boolean disconnect(Connectable<?> connected, End end);
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2019. 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.feature.connection;
import lu.list.itis.dkd.tui.content.Drawable;
import lu.list.itis.dkd.tui.feature.connection.builder.BaseConnectionBuilder;
/**
* @author nico.mack@list.lu
* @since 2.6
* @version 1.0.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public abstract class Connection implements Drawable {
protected String name;
protected Connectable<?> origin;
protected Connectable<?> ending;
/**
* Field indicating the draw priority of the Connection. <code>Integer#MAX_VALUE</code> is the
* highest priority, hence, will be drawn last. Default: <code>0</code>.
*/
protected int drawPriority = 0;
// ***************************************************************************
// * Constants *
// ***************************************************************************
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param builder
*/
// ---------------------------------------------------------------------------
public Connection(BaseConnectionBuilder<?> builder) {
this.name = builder.name;
this.drawPriority = builder.drawPriority;
}
// ---------------------------------------------------------------------------
public Connection(Connection original) {
this.name = original.name;
this.drawPriority = original.drawPriority;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body *
// ***************************************************************************
// ---------------------------------------------------------------------------
public String getName() {
return this.name;
}
// ---------------------------------------------------------------------------
/**
* Setter for setting the origin of the tether
*
* @param position
* specifies the new origin of this tether
*/
// ---------------------------------------------------------------------------
public void setOrigin(Connectable<?> connectable) {
this.origin = connectable;
}
// ---------------------------------------------------------------------------
/**
* Setter for setting the ending of the tether
*
* @param position
* specifies the new ending of this tether
*/
// ---------------------------------------------------------------------------
public void setEnding(Connectable<?> connectable) {
this.ending = connectable;
}
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public int getDrawPriority() {
return this.drawPriority;
}
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public int compareTo(Drawable drawable) {
if (drawable == null)
throw new NullPointerException();
return Integer.compare(drawPriority, drawable.getDrawPriority());
}
// ---------------------------------------------------------------------------
@Override
public abstract Connection clone();
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of class *
// ***************************************************************************
// ---------------------------------------------------------------------------
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2019. 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.feature.connection;
import lu.list.itis.dkd.tui.feature.connection.Connectable.End;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.TreeMultimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
/**
* @author nico.mack@list.lu
* @since 2.6
* @version 1.0.0
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class ConnectionManager {
private Connectable<?> connectable;
private boolean exclusive;
private List<Class<? extends Connectable<?>>> accepted;
private List<Class<? extends Connectable<?>>> rejected;
private Multimap<Connectable<?>, Connection> connections;
// ***************************************************************************
// * Constants
// ***************************************************************************
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class.getSimpleName());
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s)
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param connectable
* @param exclusive
*/
// ---------------------------------------------------------------------------
public ConnectionManager(Connectable<?> connectable, boolean exclusive) {
this.connectable = connectable;
this.exclusive = exclusive;
this.accepted = new ArrayList<>();
this.rejected = new ArrayList<>();
this.connections = Multimaps.synchronizedSortedSetMultimap(TreeMultimap.create());
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives *
// ***************************************************************************
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param acceptedClazz
*/
// ---------------------------------------------------------------------------
public void accept(Class<? extends Connectable<?>> acceptedClazz) {
if (!this.accepted.contains(acceptedClazz)) {
this.accepted.add(acceptedClazz);
}
}
// ---------------------------------------------------------------------------
/**
* @param rejectedClazz
*/
// ---------------------------------------------------------------------------
public void reject(Class<? extends Connectable<?>> rejectedClazz) {
if (!this.rejected.contains(rejectedClazz)) {
this.rejected.add(rejectedClazz);
}
}
// ---------------------------------------------------------------------------
/**
* @param candidate
* @return
*/
// ---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public boolean canConnect(Connectable<?> candidate) {
boolean passed = false;
Class<? extends Connectable<?>> candidateClazz = (Class<? extends Connectable<?>>) candidate.getClass();
passed = (this.accepted.isEmpty() || this.accepted.contains(candidateClazz));
passed &= (this.rejected.isEmpty() || !this.rejected.contains(candidateClazz));
return passed;
}
// ---------------------------------------------------------------------------
public List<Connection> getConnections() {
return new ArrayList<>(this.connections.values());
}
// ---------------------------------------------------------------------------
/**
* @return
*/
// ---------------------------------------------------------------------------
public boolean isConnected() {
return !this.connections.isEmpty();
}
// ---------------------------------------------------------------------------
/**
* @return
*/
// ---------------------------------------------------------------------------
public boolean isExclusive() {
return this.exclusive;
}
// ---------------------------------------------------------------------------
/**
* @return
*/
// ---------------------------------------------------------------------------
public int getNumberOfConnections() {
return this.connections.values().size();
}
// ---------------------------------------------------------------------------
/**
* @param candidate
* @param connection
* @param end
* @return
*/
// ---------------------------------------------------------------------------
public boolean connect(Connectable<?> candidate, Connection connection, End end) {
boolean connectionEstablished = false;
boolean isConnectable = !(this.exclusive && this.isConnected()) && canConnect(candidate);
if (isConnectable && !this.connections.containsEntry(candidate, connection)) {
switch (end) {
case ORIGIN:
connection.setOrigin(this.connectable);
candidate.connect(connectable, connection, Connectable.End.ENDING);
break;
case ENDING:
connection.setEnding(this.connectable);
break;
default:
LOGGER.error("Undefined Connection End {} specified!", end); //$NON-NLS-1$
}
connectionEstablished = this.connections.put(candidate, connection);
}
return connectionEstablished;
}
// ---------------------------------------------------------------------------
/**
* @param connected
* @param end
* @return
*/
// ---------------------------------------------------------------------------
public boolean disconnect(Connectable<?> connected, End end) {
boolean connectionTornDown = false;
if (this.connections.containsKey(connected)) {
this.connections.removeAll(connected);
if (end == Connectable.End.ORIGIN) {
connectionTornDown = connected.disconnect(this.connectable, Connectable.End.ENDING);
} else {
connectionTornDown = true;
}
}
return connectionTornDown;
}
// ---------------------------------------------------------------------------
/**
* @param canvas
*/
// ---------------------------------------------------------------------------
public void updateConnections(Graphics2D canvas) {
for (Connection connection : this.connections.values()) {
connection.paint(canvas);
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class
// ***************************************************************************
// ---------------------------------------------------------------------------
}
package lu.list.itis.dkd.tui.feature.connection;
import lu.list.itis.dkd.tui.feature.connection.builder.BaseSplineBuilder;
import lu.list.itis.dkd.tui.utility.AngleUtils;
import lu.list.itis.dkd.tui.utility.Calibration;
import lu.list.itis.dkd.tui.utility.Linear;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.utility.PolarCoordinateHelper;
import lu.list.itis.dkd.tui.utility.ScreenCoordinates;
import lu.list.itis.dkd.tui.utility.ShapeUtils;
import lu.list.itis.dkd.tui.widget.corona.ShapeFactory;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class Spline extends Connection implements Cloneable {
protected double originOffsetAngle;
protected double endingOffsetAngle;
protected int interpolationSteps;
protected Polygon spline;
protected float strokeWidth;
protected Color strokeColour;
private Linear first;
private Linear second;
private Point intersection;
private Shape marker = ShapeFactory.buildCircle(2);
// ***************************************************************************
// * Constants *
// ***************************************************************************
private static final double SCREEN_DIAGONAL = Math.sqrt(Math.pow(Calibration.getScreenWidth(), 2) + Math.pow(Calibration.getScreenHeight(), 2));
// ---------------------------------------------------------------------------
// ***************************************************************************