Commit 54362a4a authored by Nico Mack's avatar Nico Mack

Cleanup of Bootstrapping Code. Implemented Templating Feature

parent b2556811
......@@ -3,3 +3,4 @@ bin
dist
target
/.DS_Store
......@@ -67,6 +67,37 @@ STOP_NODE=stop
STYLE_NODE=style
TEXT_NODE=text
TITLE_NODE=title
BEGIN_NODE=begin
END_NODE=end
TETHERABLE_NODE=tetherable
DISTANCE_NODE=distance
DRAGGABLE_NODE=draggable
EXCLUSIVE_NODE=exclusive
ROTATE_WITH_TETHER_NODE=rotateWithTether
DISPLAY_TIME_NODE=displayTime
FADE_IN_TIME_NODE=fadeInTime
FADE_OUT_TIME_NODE=fadeOutTime
INDEX_NODE=index
INNER_RADIUS_NODE=innerRadius
OUTER_RADIUS_NODE=outerRadius
START_ANGLE_NODE=startAngle
ARC_SPAN_NODE=arcSpan
GAP_NODE=gap
STROKE_WIDTH_NODE=strokeWidth
FILL_COLOUR_ELEMENT=fillColour
TEXT_COLOUR_ELEMENT=textColour
STROKE_COLOUR_ELEMENT=strokeColour
SELECTED_ELEMENT=selected
DESELECTED_ELEMENT=deselected
RGB_COLOUR_NODE=rgb
PERSISTENT_NODE=persistent
PRESELECT_NODE=preselect
TEMPLATES_NODE=templates
TEMPLATE_NODE=template
TRIGGER_CONDITION_NODE=triggerCondition
TRIGGER_ELEMENT=trigger
TRIGGERS_NODE=triggers
......@@ -80,3 +111,18 @@ WIDTH_NODE=width
X_NODE=x
Y_NODE=y
Z_NODE=z
ZOOM_LEVEL_NODE=zoomLevel
SYNCHRONOUS_ZOOM_NODE=synchronousZoom
CENTER_ON_ZOOM_NODE=centerOnZoom
ASSIGNABLE_NODE=assignable
CORNER_RADIUS_NODE=cornerRadius
CIRCLE_SIZE_NODE=circleSize
SQUARE_SIZE_NODE=squareSize
TRIANGLE_SIZE_NODE=triangleSize
NAME_ATTRIBUTE=name
REPEAT_ATTRIBUTE=repeat
INDEX_ATTRIBUTE=index
COUNT_ATTRIBUTE=count
......@@ -28,6 +28,9 @@ import lu.list.itis.dkd.tui.utility.GlobalContext;
import lu.list.itis.dkd.tui.utility.IdMapper;
import lu.list.itis.dkd.tui.utility.PropertiesFetcher;
import org.pushingpixels.trident.Timeline.RepeatBehavior;
import org.pushingpixels.trident.TridentConfig;
import org.pushingpixels.trident.swing.SwingRepaintTimeline;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -126,6 +129,9 @@ public abstract class TangibleApplication {
System.exit(42);
}
TridentConfig.getInstance().setPulseSource(new TridentConfig.FixedRatePulseSource(40));
new SwingRepaintTimeline(TangibleApplication.getInterfaceManager()).playLoop(RepeatBehavior.LOOP);
GlobalContext.setGlobalContext(adapter);
IdMapper.initialise(properties);
// EventLogger.getInstance().initialise(properties);
......
......@@ -22,8 +22,10 @@ package lu.list.itis.dkd.tui;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.content.Content;
import lu.list.itis.dkd.tui.utility.Calibration;
import lu.list.itis.dkd.tui.utility.TextHelper;
import lu.list.itis.dkd.tui.widget.BaseWidget;
import com.google.common.base.Preconditions;
......@@ -35,6 +37,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
......@@ -42,7 +45,9 @@ import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.util.Collection;
import java.util.Properties;
import javax.swing.JComponent;
......@@ -97,24 +102,32 @@ public class TangibleInterfaceManager extends JComponent {
/** A reference to the hosting application to access different managers if needed. */
private volatile TangibleApplication tangibleApplication;
private Area clippingRegion;
private Area fullScreenArea;
/**
* Constructor initialising several fields and setting up the basic interface.
*
* @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.
*/
public TangibleInterfaceManager(TangibleApplication applicationContext, Properties properties) {
tangibleApplication = applicationContext;
interfaceProperties = properties;
// logger = applicationLogger;
System.setProperty("sun.awt.noerasebackground", "true");
windowWidth = Integer.parseInt(properties.getProperty("windowWidth")); //$NON-NLS-1$
windowHeight = Integer.parseInt(properties.getProperty("windowHeight")); //$NON-NLS-1$
fullscreen = Boolean.parseBoolean(properties.getProperty("fullscreen")); //$NON-NLS-1$
fullScreenArea = new Area(new Rectangle(0, 0, windowWidth, windowHeight));
clippingRegion = new Area();
clippingRegion.add(fullScreenArea);
title = interfaceProperties.getProperty("frameTitle", "TangibleApplication"); //$NON-NLS-1$ //$NON-NLS-2$
String calibration = interfaceProperties.getProperty("calibrationFileURI"); //$NON-NLS-1$
......@@ -168,6 +181,7 @@ public class TangibleInterfaceManager extends JComponent {
/** Method used to show the application's main frame. */
public void showWindow() {
setBounds();
frame.setIgnoreRepaint(true);
frame.getContentPane().setBackground(Color.BLACK);
if (fullscreen) {
......@@ -277,11 +291,23 @@ public class TangibleInterfaceManager extends JComponent {
public void paint(@Nullable Graphics canvas) {
Preconditions.checkArgument(canvas != null, "The Graphics context cannot be null!"); //$NON-NLS-1$
// RepaintManager repaintManager = RepaintManager.currentManager(this);
// boolean oldState = repaintManager.isDoubleBufferingEnabled();
// repaintManager.setDoubleBufferingEnabled(false);
if (calibrationMode) {
updateCalibration(canvas);
} else {
update(canvas);
}
// repaintManager.setDoubleBufferingEnabled(oldState);
}
@Override
public void paintComponent(Graphics canvas) {
this.paint(canvas);
}
/**
......@@ -323,6 +349,7 @@ public class TangibleInterfaceManager extends JComponent {
}
/**
* Method used to redraw the interface. It will issue a call to each of the tangibles to redraw
* themselves.
......@@ -335,20 +362,53 @@ public class TangibleInterfaceManager extends JComponent {
public void update(@Nullable Graphics canvas) {
Preconditions.checkArgument(canvas != null, "The canvas to draw on cannot be null!"); //$NON-NLS-1$
Graphics2D canvas2D = (Graphics2D) canvas;
// Shape originalClip = canvas2D.getClip();
// if (!clippingRegion.isEmpty())
// canvas2D.clip(clippingRegion);
// clippingRegion.reset();
canvas2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
canvas2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
canvas2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
canvas2D.setColor(Color.white);
canvas2D.fillRect(0, 0, Calibration.getScreenWidth(), Calibration.getScreenHeight());
long elapsed = System.currentTimeMillis();
if (tangibleApplication.getContentManager() != null) {
tangibleApplication.getContentManager().getDrawableContents().forEach(content -> content.paint(canvas2D));
Collection<Content> contents = tangibleApplication.getContentManager().getDrawableContents();
contents.forEach(content -> content.paint(canvas2D));
// contents.forEach(content -> clippingRegion.add(content.getClippingRegion()));
}
elapsed = System.currentTimeMillis() - elapsed;
// logger.info("Drawing Content took {} ms!", elapsed);
// canvas2D.setClip(fullScreenArea);
// canvas2D.setPaint(Color.BLACK);
// canvas2D.setStroke(new BasicStroke(1));
// canvas2D.draw(new GeneralPath(clippingRegion));
elapsed = System.currentTimeMillis();
if (tangibleApplication.getObjectManager() != null) {
TangibleObjectManager.getWidgets().forEach(widget -> widget.paint(canvas2D));
Collection<BaseWidget> widgets = TangibleObjectManager.getWidgets();
widgets.forEach(widget -> widget.paint(canvas2D));
// widgets.forEach(widget -> clippingRegion.add(widget.getClippingRegion()));
}
elapsed = System.currentTimeMillis() - elapsed;
// logger.info("Drawing Widgets took {} ms!", elapsed);
// canvas2D.setPaint(Color.BLACK);
// canvas2D.setStroke(new BasicStroke(1));
// canvas2D.draw(new GeneralPath(clippingRegion));
// canvas2D.setClip(originalClip);
}
/**
......
......@@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
......@@ -198,6 +199,29 @@ public abstract class TangibleObjectManager {
return blobMap.get(identifier);
}
/**
* Method for retrieving all widgets of a particular class.
*
* @param <T>
* The type of the class of widget to retrieve.
* @param _class
* The class of widgets to retrieve.
* @return A list of widgets matching the criterion.
*/
@SuppressWarnings("unchecked")
public <T> List<T> getWidgets(Class<T> _class) {
List<T> results = new ArrayList<>();
for (BaseWidget widget : objectMap.values()) {
if (_class.isAssignableFrom(widget.getClass())) {
results.add((T) widget);
}
}
return results;
}
/**
* Method for retrieving all handle IDs associated with the {@link BaseWidget}.
*
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. 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.bootstrapping;
import org.jdom2.Element;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public interface BootstrapCallback {
/**
* @param node
* @param context
* @return
*/
public BootstrapContext preInstantiation(Element node, BootstrapContext context);
/**
* @param node
* @param context
* @return
*/
public BootstrapContext postInstantiation(Element node, BootstrapContext context);
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. 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.bootstrapping;
import java.util.HashMap;
import java.util.Map;
/**
* Contains properties allowing to parameterize the bootstrapping process. Properties can be
* compared to variables and are useful when creating bootstrap templates.
*
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class BootstrapContext {
private Map<String, Object> context;
/**
* creates a new empty bootstrap context.
*/
public BootstrapContext() {
context = new HashMap<>();
}
/**
* sets the property identified by identifier to the specified value.
*
* @param identifier
* specifies the name of the property to set.
* @param value
* specifies the value to set the property to.
* @return the previous (old) value the property had before the set. Will return
* <code>null</code> if property wasn't set before.
*/
public Object setProperty(String identifier, Object value) {
return context.put(identifier, value);
}
/**
* Returns the value of the property identified by the specified identifier if its value was
* previously set.
*
* @param identifier
* specifies the name of the property to get the value of.
* @return the value of the specified property if it exists, <code>null</code> otherwise.
*/
public Object getProperty(String identifier) {
return context.get(identifier);
}
/**
* Checks whether a property matching the specified identifier exists.
*
* @param identifier
* specifies the name of the property to check the existence of.
* @return <code>true</code> if specified property exists, <code>false</code> otherwise.
*/
public boolean hasProperty(String identifier) {
return context.containsKey(identifier);
}
/**
* @return the number of properties defined in context
*/
public int size() {
return context.size();
}
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. 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.bootstrapping;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.utility.CoordinateState;
import lu.list.itis.dkd.tui.utility.Externalization;
import lu.list.itis.dkd.tui.utility.Point;
import org.jdom2.Element;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class CoordinateStateBootstrapper {
/**
* @param rootNode
* @param coordinates
* @return
* @throws BuildException
*/
public static @Nullable CoordinateState getCoordinateState(Element rootNode, Point coordinates) throws BuildException {
return getCoordinateState(rootNode, coordinates, null, null);
}
/**
* @param rootNode
* @param coordinates
* @param context
* @param callback
* @return
* @throws BuildException
*/
public static @Nullable CoordinateState getCoordinateState(Element rootNode, Point coordinates, BootstrapContext context, BootstrapCallback callback) throws BuildException {
CoordinateState state = null;
if (rootNode == null)
return null;
String stateAsString = BootstrappingUtils.getContentAsString(rootNode, Externalization.STATE_NODE, BootstrappingUtils.OPTIONAL, "CameraCoordinates", context); //$NON-NLS-1$
try {
Class<?> stateClass = Class.forName(Point.class.getPackage().getName() + "." + stateAsString); //$NON-NLS-1$
Constructor<?> stateConstructor = stateClass.getConstructor(new Class[] {Point.class, java.lang.Float.TYPE, java.lang.Float.TYPE});
state = (CoordinateState) stateConstructor.newInstance(new Object[] {coordinates, coordinates.x, coordinates.y});
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new BuildException("The state of the point cannot be resolved for " + stateAsString, e); //$NON-NLS-1$
}
return state;
}
}
......@@ -25,8 +25,8 @@ import lu.list.itis.dkd.tui.utility.Externalization;
import lu.list.itis.dkd.tui.widget.corona.Corona;
import lu.list.itis.dkd.tui.widget.corona.builder.CoronaBuilder;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import org.jdom2.Element;
......@@ -68,12 +68,66 @@ public class CoronaBootstrapper {
* Thrown if building the corona did not succeed due to an internal error such as a
* violation of precondition(s) or unavailable but required resources.
*/
private static Corona getCorona(Element coronaNode) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, BuildException {
// private static Corona getCorona(Element coronaNode) throws ClassNotFoundException,
// InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException,
// IllegalArgumentException, InvocationTargetException, BuildException {
// Element type = coronaNode.getChild(Externalization.TYPE_NODE);
// Class<?> builder = Class.forName(Externalization.CORONA_BUILDER_NAMESPACE +
// Externalization.NAMESPACE_SEPARATOR + type.getValue() +
// Externalization.BUILDER_CLASS_POSTFIX);
// @SuppressWarnings("unchecked")
// Constructor<CoronaBuilder<?>> constructor = (Constructor<CoronaBuilder<?>>)
// builder.getConstructor(new Class[] {Element.class});
// return constructor.newInstance(new Object[] {coronaNode}).build();
// }
/**
* Method used to determine the appropriate builder for a given object and then issue a build
* call.
*
* @param bootstrapContext
* @param callback
*
* @param coronaNode
* The node from a larger document that contains, as children, all the necessary
* information to resolve the correct builder and build the final widget.
* @return The final widget 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 Corona buildCoronaFromElement(Element coronaNode, BootstrapContext bootstrapContext, BootstrapCallback callback) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, BuildException {
Corona instance = null;
Element type = coronaNode.getChild(Externalization.TYPE_NODE);
Class<?> builder = Class.forName(Externalization.CORONA_BUILDER_NAMESPACE + Externalization.NAMESPACE_SEPARATOR + type.getValue() + Externalization.BUILDER_CLASS_POSTFIX);
@SuppressWarnings("unchecked")
Constructor<CoronaBuilder<?>> constructor = (Constructor<CoronaBuilder<?>>) builder.getConstructor(new Class[] {Element.class});
return constructor.newInstance(new Object[] {coronaNode}).build();
if ((bootstrapContext == null) || (bootstrapContext.size() == 0)) {
Constructor<CoronaBuilder<?>> constructor = (Constructor<CoronaBuilder<?>>) builder.getConstructor(new Class[] {Element.class});
instance = constructor.newInstance(new Object[] {coronaNode}).build();
} else {
Constructor<CoronaBuilder<?>> constructor = (Constructor<CoronaBuilder<?>>) builder.getConstructor(new Class[] {Element.class, BootstrapContext.class, BootstrapCallback.class});
instance = constructor.newInstance(new Object[] {coronaNode, bootstrapContext, callback}).build();
}
return instance;
}
/**
......@@ -88,12 +142,13 @@ public class CoronaBootstrapper {
* Thrown when a corona was not associated to any handle.
*/
public static Multimap<Integer, Corona> getCoronas(Element coronaRootNode) throws BuildException {
Multimap<Integer, Corona> coronas = TreeMultimap.create();
// Multimap<Integer, Corona> coronas = TreeMultimap.create();
Multimap<Integer, Corona> coronas = ArrayListMultimap.create();
for (Element coronaNode : coronaRootNode.getChildren(Externalization.CORONA_NODE)) {
try {
int handleId = Integer.parseInt(coronaNode.getChildText(Externalization.HANDLE_NODE));
coronas.put(handleId, getCorona(coronaNode));
coronas.put(handleId, buildCoronaFromElement(coronaNode, null, null));
} catch (NumberFormatException nfe) {
throw new BuildException("The corona was not associated with any handle. The handle ID may not be null!", nfe); //$NON-NLS-1$
} catch (ReflectiveOperationException roe) {
......@@ -103,4 +158,35 @@ public class CoronaBootstrapper {
return coronas;
}
/**
* Method used for bootstrapping all coronas found as child nodes of a root node. The method
* will look for child nodes names <code>corona</code>.
*
* @param coronaRootNode
* The root node that holds all <code>corona</code> child nodes.
* @param context
* @param callback
* @return A map containing all concrete {@link Corona} instances that could be build from the
* provided root node keyed to the handle ID to which they are associated. *
* @throws BuildException
* Thrown when a corona was not associated to any handle.
*/
public static Multimap<Integer, Corona> getCoronas(Element coronaRootNode, BootstrapContext context, BootstrapCallback callback) throws BuildException {
Multimap<Integer, Corona> coronas = ArrayListMultimap.create();
for (Element coronaNode : coronaRootNode.getChildren(Externalization.CORONA_NODE)) {
int handleId = BootstrappingUtils.getContentAsInteger(coronaNode, Externalization.HANDLE_NODE, BootstrappingUtils.MANDATORY, 0, context);
CoronaFactory<Corona> factory = new CoronaFactory<>(coronaNode, context, callback);
while (factory.hasNext()) {
Corona newInstance = factory.next();
coronas.put(handleId, newInstance);
}
}
return coronas;
}
}
\ No newline at end of file
/**
* Copyright Luxembourg Institute of Science and Technology, 2017. 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.bootstrapping;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.utility.Externalization;
import lu.list.itis.dkd.tui.widget.corona.Corona;
import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
* @param <C>
*/