/*
* Copyright 2004-2010 Information & Software Engineering Group (188/1)
* Institute of Software Technology and Interactive Systems
* Vienna University of Technology, Austria
*
* Licensed 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.ifs.tuwien.ac.at/dm/somtoolbox/license.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package at.tuwien.ifs.somtoolbox.apps.viewer.controls.psomserver;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import com.sun.net.httpserver.HttpServer;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolox.nodes.PLine;
import at.tuwien.ifs.commons.gui.controls.TitledCollapsiblePanel;
import at.tuwien.ifs.commons.gui.controls.swing.table.ButtonCellEditor;
import at.tuwien.ifs.commons.gui.controls.swing.table.ButtonCellRenderer;
import at.tuwien.ifs.commons.gui.controls.swing.table.ColorCellRenderer;
import at.tuwien.ifs.somtoolbox.apps.viewer.CommonSOMViewerStateData;
import at.tuwien.ifs.somtoolbox.apps.viewer.VisualizationChangeListener;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.AbstractViewerControl;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.PlaySOMPanel;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.player.PlayListListener;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.player.PlaySOMPlayer;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.player.PlayerListener;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.psomserver.httphandler.MapInformationProvider;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.psomserver.httphandler.PocketSOMConfigProvider;
import at.tuwien.ifs.somtoolbox.apps.viewer.controls.psomserver.httphandler.SongProvider;
import at.tuwien.ifs.somtoolbox.data.metadata.AudioVectorMetaData;
import at.tuwien.ifs.somtoolbox.layers.Unit;
/**
* @author Jakob Frank
* @version $Id: PocketSOMConnector.java 3900 2010-11-04 10:06:02Z frank $
*/
public class PocketSOMConnector extends AbstractViewerControl {
private static final long serialVersionUID = 1L;
private static final String START = "Listen...";
private static final String STOP = "Stop";
/**
* Timeout for GC (in sec.): {@value}
*/
private static final int LIFETIME = 15 * 60;
private ConnectorEndpoint con = null;
private JButton btnServerCtrl = null;
private JTextField txtPIN = null;
private JTextField txtPort = null;
private JScrollPane scpUsers = null;
private JTable tblUsers = null;
private UserTableModel utm = null;
private HttpServer httpServer;
private JTextField txtHttpPort;
private JButton btnHitHist;
private int[][] pathHitHist;
private JPanel pnlMerge;
private JCheckBox chkMergeDebugPrint;
public PocketSOMConnector(String title, CommonSOMViewerStateData state) {
super(title, state);
if (state != null) {
pathHitHist = new int[state.growingLayer.getXSize()][state.growingLayer.getYSize()];
for (int i = 0; i < pathHitHist.length; i++) {
for (int j = 0; j < pathHitHist[i].length; j++) {
pathHitHist[i][j] = 0;
}
}
initialize();
}
}
private void initialize() {
GridBagConstraints gridBagConstraints31 = new GridBagConstraints();
gridBagConstraints31.fill = GridBagConstraints.BOTH;
gridBagConstraints31.gridy = 4;
gridBagConstraints31.weightx = 1.0;
gridBagConstraints31.weighty = 1.0;
gridBagConstraints31.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints31.insets = new Insets(6, 0, 0, 0);
gridBagConstraints31.gridx = 0;
GridBagConstraints gridBagConstraints21 = new GridBagConstraints();
gridBagConstraints21.fill = GridBagConstraints.BOTH;
gridBagConstraints21.gridy = 0;
gridBagConstraints21.weightx = 1.0;
gridBagConstraints21.gridx = 1;
GridBagConstraints gridBagConstraints11 = new GridBagConstraints();
gridBagConstraints11.fill = GridBagConstraints.BOTH;
gridBagConstraints11.gridy = 1;
gridBagConstraints11.weightx = 1.0;
gridBagConstraints11.gridx = 1;
GridBagConstraints gridBagConstraints99 = new GridBagConstraints();
gridBagConstraints99.fill = GridBagConstraints.BOTH;
gridBagConstraints99.gridy = 2;
gridBagConstraints99.weightx = 1.0;
gridBagConstraints99.gridx = 1;
GridBagConstraints gridBagConstraints41 = new GridBagConstraints();
gridBagConstraints41.fill = GridBagConstraints.BOTH;
gridBagConstraints41.gridy = 5;
gridBagConstraints41.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints41.weightx = 1.0;
gridBagConstraints41.insets = new Insets(6, 0, 0, 0);
gridBagConstraints41.gridx = 0;
GridBagConstraints gridBagConstraints3 = new GridBagConstraints();
gridBagConstraints3.gridx = 0;
gridBagConstraints3.insets = new Insets(0, 0, 0, 2);
gridBagConstraints3.anchor = GridBagConstraints.EAST;
gridBagConstraints3.gridy = 0;
JLabel lblPort = new JLabel();
lblPort.setText("Control Port:");
GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
gridBagConstraints2.gridx = 2;
gridBagConstraints2.gridheight = 1;
gridBagConstraints2.gridy = 0;
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.insets = new Insets(0, 0, 0, 2);
gridBagConstraints.anchor = GridBagConstraints.EAST;
gridBagConstraints.gridy = 1;
GridBagConstraints gridBagConstraints98 = new GridBagConstraints();
gridBagConstraints98.gridx = 0;
gridBagConstraints98.insets = new Insets(0, 0, 0, 2);
gridBagConstraints98.anchor = GridBagConstraints.EAST;
gridBagConstraints98.gridy = 2;
GridBagConstraints gridBagConstraints02 = new GridBagConstraints();
gridBagConstraints02.gridx = 2;
gridBagConstraints02.gridy = 1;
GridBagConstraints gridBagConstraints03 = new GridBagConstraints();
gridBagConstraints03.gridx = 2;
gridBagConstraints03.gridy = 2;
GridBagConstraints gridBagConstraints3x = new GridBagConstraints();
gridBagConstraints3x.gridx = 0;
gridBagConstraints3x.gridy = 3;
gridBagConstraints3x.gridwidth = GridBagConstraints.REMAINDER;
gridBagConstraints3x.fill = GridBagConstraints.NONE;
gridBagConstraints3x.anchor = GridBagConstraints.EAST;
JLabel lblPIN = new JLabel();
lblPIN.setText("Master PIN:");
JLabel lblHttpPort = new JLabel();
lblHttpPort.setText("Http Port:");
JPanel mainP = new JPanel();
mainP.setLayout(new GridBagLayout());
mainP.add(lblPort, gridBagConstraints3);
mainP.add(getTxtPort(), gridBagConstraints21);
mainP.add(lblHttpPort, gridBagConstraints);
mainP.add(getTxtHttpPort(), gridBagConstraints11);
mainP.add(lblPIN, gridBagConstraints98);
mainP.add(getTxtPIN(), gridBagConstraints99);
mainP.add(getBtnServerCtrl(), gridBagConstraints3x);
mainP.add(getScpUsers(), gridBagConstraints31);
mainP.add(getPnlMerge(), gridBagConstraints41);
this.setContentPane(mainP);
}
protected boolean stopServer() {
if (httpServer != null) {
httpServer.stop(2);
httpServer = null;
}
if (con == null) {
return false;
}
con.shutdown();
con = null;
txtPort.setEnabled(true);
txtPIN.setEnabled(true);
txtHttpPort.setEnabled(true);
return true;
}
protected boolean startServer() {
try {
// Endpoint
txtPort.setEnabled(false);
txtPIN.setEnabled(false);
int port = Integer.parseInt(txtPort.getText());
if (con != null) {
return false;
}
con = new ConnectorEndpoint(port, txtPIN.getText());
con.start();
// Config
txtHttpPort.setEnabled(false);
if (httpServer == null) {
String musicContext = "/music";
String configContext = "/eps";
int httpP = 8000;
try {
httpP = Integer.parseInt(txtHttpPort.getText());
} catch (NumberFormatException e) {
}
PocketSOMConfigProvider ps = new PocketSOMConfigProvider(state, port, musicContext);
httpServer = HttpServer.create(new InetSocketAddress(httpP), 0);
httpServer.setExecutor(null);
httpServer.createContext(configContext, ps);
httpServer.createContext("/ePocketSOM", ps);
httpServer.createContext(musicContext, new SongProvider(state, musicContext));
httpServer.createContext("/", new MapInformationProvider(state, musicContext, configContext));
httpServer.start();
}
return true;
} catch (Exception e) {
txtPort.setEnabled(true);
txtPIN.setEnabled(true);
txtHttpPort.setEnabled(true);
return false;
}
}
protected PocketSOMConnector(String title, CommonSOMViewerStateData state, LayoutManager layout) {
this(title, state);
}
/**
* This method initializes btnServerCtrl
*
* @return javax.swing.JButton
*/
private JButton getBtnServerCtrl() {
if (btnServerCtrl == null) {
btnServerCtrl = new JButton();
btnServerCtrl.setText(START);
btnServerCtrl.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals(START)) {
if (startServer()) {
btnServerCtrl.setText(STOP);
btnServerCtrl.setActionCommand(STOP);
}
} else {
if (stopServer()) {
btnServerCtrl.setText(START);
btnServerCtrl.setActionCommand(START);
}
}
}
});
}
return btnServerCtrl;
}
private JButton getBtnHitHist() {
if (btnHitHist == null) {
btnHitHist = new JButton("Show HitHist");
btnHitHist.setToolTipText("Show the path hit histogram");
btnHitHist.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
state.mapPNode.showHistogramOverlayVisualization(pathHitHist, 0); // 2nd param is visualization
// index
}
});
}
return btnHitHist;
}
/**
* This method initializes txtPIN
*
* @return javax.swing.JTextField
*/
private JTextField getTxtPIN() {
if (txtPIN == null) {
txtPIN = new JTextField();
// txtPIN.setColumns(4);
txtPIN.setText("1234");
}
return txtPIN;
}
/**
* This method initializes txtPort
*
* @return javax.swing.JTextField
*/
private JTextField getTxtPort() {
if (txtPort == null) {
txtPort = new JTextField();
// txtPort.setColumns(6);
txtPort.setText("9619");
}
return txtPort;
}
private JTextField getTxtHttpPort() {
if (txtHttpPort == null) {
txtHttpPort = new JTextField();
// txtHttpPort.setColumns(6);
txtHttpPort.setText("8000");
}
return txtHttpPort;
}
/**
* This method initializes scpUsers
*
* @return javax.swing.JScrollPane
*/
private JScrollPane getScpUsers() {
if (scpUsers == null) {
scpUsers = new JScrollPane(getTblUsers());
scpUsers.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
getTblUsers().clearSelection();
}
});
scpUsers.setPreferredSize(new Dimension(state.controlElementsWidth / 2, Math.max(
getTblUsers().getRowCount(), 4) * getTblUsers().getRowHeight()));
}
return scpUsers;
}
/**
* This method initializes tblUsers
*
* @return javax.swing.JTable
*/
private JTable getTblUsers() {
if (tblUsers == null) {
tblUsers = new JTable();
tblUsers.setDefaultRenderer(Color.class, new ColorCellRenderer(true));
tblUsers.setDefaultRenderer(JButton.class, new ButtonCellRenderer());
tblUsers.setDefaultEditor(JButton.class, new ButtonCellEditor());
utm = new UserTableModel();
tblUsers.setModel(utm);
tblUsers.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
utm.setSelected(tblUsers.getSelectedRows());
}
});
tblUsers.setFillsViewportHeight(true);
// tblUsers.setPreferredSize(new Dimension(state.controlElementsWidth));
}
return tblUsers;
}
private JPanel getPnlMerge() {
if (pnlMerge == null) {
pnlMerge = new TitledCollapsiblePanel("Advanced", true);
Container c = ((TitledCollapsiblePanel) pnlMerge).getContentPane();
c.setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.gridy = 0;
gc.gridwidth = GridBagConstraints.RELATIVE;
c.add(new JLabel("Merge selected Paths:"), gc);
gc.gridwidth = GridBagConstraints.REMAINDER;
gc.anchor = GridBagConstraints.EAST;
c.add(getChkMergeDebugPrint(), gc);
gc.gridy++;
gc.gridwidth = 2;
gc.anchor = GridBagConstraints.WEST;
gc.fill = GridBagConstraints.BOTH;
c.add(getBtnMergeUnitBased(), gc);
c.add(getBtnMergeLineBased(), gc);
gc.gridy++;
c.add(getBtnMergePathInputSpace(), gc);
c.add(getBtnMergePathMapSpace(), gc);
gc.gridy++;
c.add(getBtnMergeConcat(), gc);
c.add(getBtnReversePath(), gc);
gc.gridy++;
c.add(getBtnHighlightPath(), gc);
c.add(getBtnHitHist(), gc);
gc.gridy++;
c.add(getBtnClearMerges(), gc);
c.add(getBtnUsePath(), gc);
getTblUsers().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
// boolean status = getTblUsers().getSelectedRowCount() > 1;
// pnlMerge.setEnabled(status);
// for (Component c : pnlMerge.getComponents()) {
// c.setEnabled(status);
// }
}
});
// Initially disabled
// pnlMerge.setEnabled(false);
// for (Component c : pnlMerge.getComponents()) {
// c.setEnabled(false);
// }
}
return pnlMerge;
}
private PathMerger pathMerger = null;
private JButton btnMergeUnitBased;
private JButton btnMergeLineBased;
private JButton btnHighlightPath;
private JButton btnMergePathMapSpace;
private JButton btnMergePathInputSpace;
private JButton btnReversePath;
private JButton btnClearMerges;
private JButton btnMergeConcat;
private JButton btnUsePath;
private JButton getBtnMergeUnitBased() {
if (btnMergeUnitBased == null) {
btnMergeUnitBased = new JButton("unitBasedMerge");
btnMergeUnitBased.setMargin(SMALL_INSETS);
btnMergeUnitBased.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (pathMerger == null) {
pathMerger = new PathMerger(state.mapPNode);
}
pathMerger.setDebug(getChkMergeDebugPrint().isSelected());
int[] rows = tblUsers.getSelectedRows();
PNode[] nodes = new PNode[rows.length];
for (int i = 0; i < rows.length; i++) {
nodes[i] = utm.getRow(rows[i]).node;
}
pathMerger.unitBasedMerge(nodes);
}
});
}
return btnMergeUnitBased;
}
private JButton getBtnMergeLineBased() {
if (btnMergeLineBased == null) {
btnMergeLineBased = new JButton("lineBasedMerge");
btnMergeLineBased.setMargin(SMALL_INSETS);
btnMergeLineBased.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (pathMerger == null) {
pathMerger = new PathMerger(state.mapPNode);
}
pathMerger.setDebug(getChkMergeDebugPrint().isSelected());
int[] rows = tblUsers.getSelectedRows();
PNode[] nodes = new PNode[rows.length];
for (int i = 0; i < rows.length; i++) {
nodes[i] = utm.getRow(rows[i]).node;
}
pathMerger.lineBasedMerge(nodes);
}
});
}
return btnMergeLineBased;
}
private JButton getBtnHighlightPath() {
if (btnHighlightPath == null) {
btnHighlightPath = new JButton("Highlight");
btnHighlightPath.setMargin(SMALL_INSETS);
btnHighlightPath.addActionListener(new ActionListener() {
boolean currentStatus = false;
@Override
public void actionPerformed(ActionEvent e) {
currentStatus = !currentStatus;
PathMerger pm = new PathMerger(state.mapPNode, false);
PNode node = utm.getRow(tblUsers.getSelectedRow()).node;
pm.highlightIntersectingUnits(node, currentStatus);
}
});
}
return btnHighlightPath;
}
private JButton getBtnMergePathMapSpace() {
if (btnMergePathMapSpace == null) {
btnMergePathMapSpace = new JButton("New Map Interm");
btnMergePathMapSpace.setMargin(SMALL_INSETS);
btnMergePathMapSpace.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (pathMerger == null) {
pathMerger = new PathMerger(state.mapPNode);
}
pathMerger.setDebug(getChkMergeDebugPrint().isSelected());
int[] rows = tblUsers.getSelectedRows();
PNode[] nodes = new PNode[rows.length];
for (int i = 0; i < rows.length; i++) {
nodes[i] = utm.getRow(rows[i]).node;
}
pathMerger.newIntermediateMapMerge(nodes);
}
});
}
return btnMergePathMapSpace;
}
private JButton getBtnMergePathInputSpace() {
if (btnMergePathInputSpace == null) {
btnMergePathInputSpace = new JButton("New IS Interm");
btnMergePathInputSpace.setMargin(SMALL_INSETS);
btnMergePathInputSpace.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (pathMerger == null) {
pathMerger = new PathMerger(state.mapPNode);
}
pathMerger.setDebug(getChkMergeDebugPrint().isSelected());
int[] rows = tblUsers.getSelectedRows();
PNode[] nodes = new PNode[rows.length];
for (int i = 0; i < rows.length; i++) {
nodes[i] = utm.getRow(rows[i]).node;
}
pathMerger.newIntermediateInputSpaceMerge(nodes);
}
});
}
return btnMergePathInputSpace;
}
private JButton getBtnReversePath() {
if (btnReversePath == null) {
btnReversePath = new JButton("Reverse Path");
btnReversePath.setMargin(SMALL_INSETS);
btnReversePath.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] rows = tblUsers.getSelectedRows();
for (int row : rows) {
utm.getRow(row).reversePaths();
}
}
});
}
return btnReversePath;
}
private JButton getBtnClearMerges() {
if (btnClearMerges == null) {
btnClearMerges = new JButton("Clear");
btnClearMerges.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (pathMerger == null) {
pathMerger = new PathMerger(state.mapPNode);
}
pathMerger.setDebug(getChkMergeDebugPrint().isSelected());
int[] rows = tblUsers.getSelectedRows();
PNode[] nodes = new PNode[rows.length];
for (int i = 0; i < rows.length; i++) {
nodes[i] = utm.getRow(rows[i]).node;
}
pathMerger.deleteAllDrawnStuff();
state.mapPNode.clearHistogramOverlayVisualization();
for (int i = 0; i < pathHitHist.length; i++) {
for (int j = 0; j < pathHitHist[i].length; j++) {
pathHitHist[i][j] = 0;
}
}
}
});
}
return btnClearMerges;
}
private JButton getBtnMergeConcat() {
if (btnMergeConcat == null) {
btnMergeConcat = new JButton("Concat");
btnMergeConcat.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (pathMerger == null) {
pathMerger = new PathMerger(state.mapPNode);
}
pathMerger.setDebug(getChkMergeDebugPrint().isSelected());
int[] rows = tblUsers.getSelectedRows();
PNode[] nodes = new PNode[rows.length];
for (int i = 0; i < rows.length; i++) {
nodes[i] = utm.getRow(rows[i]).node;
}
pathMerger.concatPaths(nodes);
}
});
}
return btnMergeConcat;
}
private JButton getBtnUsePath() {
if (btnUsePath == null) {
btnUsePath = new JButton("Use Path");
btnUsePath.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (pathMerger == null) {
return;
}
PLine border = pathMerger.getBorderLine();
if (border == null) {
return;
}
PNode p = utm.addPath("Border", Color.CYAN);
p.addChild(border);
}
});
}
return btnUsePath;
}
private JCheckBox getChkMergeDebugPrint() {
if (chkMergeDebugPrint == null) {
chkMergeDebugPrint = new JCheckBox("Debug");
}
return chkMergeDebugPrint;
}
/**
* @deprecated should not be started directly
* @param args Commandline args
*/
@Deprecated
public static void main(String[] args) {
try {
PocketSOMConnector psc = new PocketSOMConnector("TEST", null);
String eps = "/eps/";
String data = "/music/";
HttpServer s = HttpServer.create(new InetSocketAddress(8000), 0);
s.setExecutor(null);
s.createContext(eps, new PocketSOMConfigProvider(psc.state, s.getAddress().getPort() + 5, data));
s.createContext("/", new MapInformationProvider(psc.state, data, eps));
s.start();
JOptionPane.showMessageDialog(null, "Stop");
s.stop(5);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* The ConnectorEndpoint, receiving Paths and PlayLists.
*
* @author Jakob Frank
*/
private class ConnectorEndpoint extends Thread {
boolean running;
ServerSocket server;
// Vector because Vector is synchronized!
private Vector<Socket> notificationReceivers;
private PlayerListener _PlayerListener;
private PlayListListener _PlayListListener;
private VisualizationChangeListener _VisualizationChangeListener;
private final String pin;
private ConnectorEndpoint(int port, String pin) throws Exception {
running = false;
this.pin = pin;
server = new ServerSocket(port);
this.setName(this.getClass().getSimpleName() + " (Port " + port + ", PIN: " + pin + ")");
_PlayerListener = new PlayerListener() {
@Override
public void playStarted(int mode, AudioVectorMetaData song) {
informListeners("currentsong " + song.getID());
}
@Override
public void playStopped(int reason, AudioVectorMetaData song) {
informListeners("currentsong");
}
};
_PlayListListener = new PlayListListener() {
@Override
public void playListContentChanged() {
informListeners("playlist changed");
}
};
_VisualizationChangeListener = new VisualizationChangeListener() {
@Override
public void visualisationChanged() {
informListeners("visualisation changed");
}
};
notificationReceivers = new Vector<Socket>();
}
@Override
public void run() {
running = true;
register4PlayerNotifications();
state.getSOMViewer().addVisualizationChangeListener(_VisualizationChangeListener);
try {
server.setSoTimeout(500);
while (running) {
try {
Socket sock = server.accept();
Worker w = new Worker(sock);
w.start();
// new Thread(w).start();
} catch (SocketTimeoutException e) {
// nop;
}
}
server.close();
} catch (IOException e) {
e.printStackTrace();
}
state.getSOMViewer().removeVisualizationChangeListener(_VisualizationChangeListener);
unregister4PlayerNotifications();
utm.clearAllPaths();
}
public void shutdown() {
running = false;
}
private void register4PlayerNotifications() {
if (state.selectionPanel instanceof PlaySOMPlayer) {
PlaySOMPlayer p = (PlaySOMPlayer) state.selectionPanel;
p.addPlayerListener(_PlayerListener);
p.addPlayListListener(_PlayListListener);
}
}
private void unregister4PlayerNotifications() {
if (state.selectionPanel instanceof PlaySOMPlayer) {
PlaySOMPlayer p = (PlaySOMPlayer) state.selectionPanel;
p.removePlayerListener(_PlayerListener);
p.removePlayListListener(_PlayListListener);
}
}
// int counter = 0;
private void informListeners(String message) {
String msg = String.format("INFO: %s%n", message);
List<Socket> deadSockets = new LinkedList<Socket>();
synchronized (notificationReceivers) {
for (Socket s : notificationReceivers) {
if (s.isClosed() || !s.isConnected() || s.isOutputShutdown()) {
System.out.printf("Socket is dead: %s:%d%n", s.getInetAddress().getHostAddress(), s.getPort());
deadSockets.add(s);
continue;
}
try {
// System.out.printf("Message to %s:%d - %s", s.getInetAddress().getHostAddress(), s.getPort(),
// msg);
s.getOutputStream().write(msg.getBytes());
s.getOutputStream().flush();
} catch (Exception e) {
System.out.printf("Socket has problems: %s:%d%n", s.getInetAddress().getHostAddress(),
s.getPort());
// e.printStackTrace();
deadSockets.add(s);
}
}
notificationReceivers.removeAll(deadSockets);
}
}
private class Worker extends Thread {// implements Runnable {
private Socket socket;
public final String CLIENT;
public final String MY_NAME = "WorkerTread";
private PlaySOMPlayer player = null;
private boolean authenticated = false;
public Worker(Socket socket) {
CLIENT = String.format("%s:%d", socket.getInetAddress().getHostAddress(), socket.getPort());
this.setName(this.toString());
this.socket = socket;
authenticated = false;
if (state.selectionPanel instanceof PlaySOMPlayer) {
player = (PlaySOMPlayer) state.selectionPanel;
}
}
@Override
public String toString() {
return String.format("%s (%s)", MY_NAME, CLIENT);
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream ps = new PrintStream(socket.getOutputStream());
for (String line = br.readLine(); line != null; line = br.readLine()) {
Date d = new Date();
System.out.printf("%tT.%tL %s %c %s%n", d, d, CLIENT, (authenticated ? '+' : '-'), line);
if (line.startsWith("Sending:")) {
String contentType = line.split(" ", 2)[1];
if (contentType.equals("Line")) {
receivePath(br);
} else if (contentType.equals("Playlist")) {
receivePlaylist(br);
}
} else if (line.startsWith("Login:")) {
clientLogin(line, br, ps);
} else if (line.startsWith("Get:")) {
sendStatusInfo(line, br, ps);
} else if (line.startsWith("Player:")) {
if (authenticated) {
playerControl(line);
}
} else if (line.matches("([Uu]n)?[Rr]egister.*")) {
manageRegistrations(line, socket, ps);
} else if (line.matches("[Qq](uit)?")) {
break;
} else if (line.startsWith("Ping")) {
if (authenticated) {
String message = "Ping";
String[] lt = line.split("[ \t]+", 2);
if (lt.length > 1) {
message = lt[1];
}
informListeners(message);
}
} else if (line.startsWith("Debug")) {
// Nop, sysout already done.
/*
* OLD } else if (false == true) { // This is old and therefore inactive! System.err.println("Illegal Communication!");
* System.err.println("Communication dump:"); System.err.println(" " + line); while ((line = br.readLine()) != null) {
* System.err.println(" " + line); } System.err.println("End Dump");
*/
} else { // This is new
System.out.println(" Illegal Communication!");
// System.err.println(" " + line);
}
}
socket.close();
} catch (IOException e) {
// e.printStackTrace();
}
}
/**
* Handle client authentication.
*/
private void clientLogin(String line, BufferedReader in, PrintStream out) {
String lType = line.split("[ \t]+")[1];
final String L_OK = "Login: OK";
final String L_ER = "Login: FAILED";
if (lType.equalsIgnoreCase("PIN")) {
try {
String pLine = in.readLine();
String receivedPin = pLine.split("[ \t]+", 2)[1];
if (receivedPin.equals(pin)) {
authenticated = true;
out.println(L_OK);
} else {
out.printf("%s %s%n", L_ER, "Wrong PIN");
}
} catch (Exception e) {
out.printf("%s %s%n", L_ER, "Communication Error");
}
} else if (lType.equalsIgnoreCase("USER")) {
try {
// String uLine = in.readLine();
// String pLine = in.readLine();
out.printf("%s Sorry, function is not implemented%n", L_ER);
} catch (Exception e) {
out.printf("%s %s%n", L_ER, "Communication Error");
}
}
}
private void manageRegistrations(String line, Socket socket, PrintStream out) {
if (line.equalsIgnoreCase("register")) {
String current = "";
if (player != null && player.isPlaying()) {
current = player.getCurrentSongID();
out.printf("INFO: currentsong %s%n", current);
} else {
out.printf("INFO: currentsong%n");
}
notificationReceivers.add(socket);
} else if (line.equalsIgnoreCase("unregister")) {
notificationReceivers.remove(socket);
}
}
private void playerControl(String line) {
// For authenticated users only!
if (!authenticated) {
return;
}
String lt[] = line.split("[ \t]+", 3);
if (lt.length < 2) {
return;
}
if (lt[1].equalsIgnoreCase("play")) {
if (lt.length > 2) {
player.startPlaying(lt[2]);
} else {
player.startPlaying();
}
} else if (lt[1].equalsIgnoreCase("stop")) {
player.stopPlaying();
} else if (lt[1].equalsIgnoreCase("next")) {
player.skipPlayer(1);
} else if (lt[1].equalsIgnoreCase("prev")) {
player.skipPlayer(-1);
}
}
private void sendStatusInfo(String line, BufferedReader in, PrintStream out) {
String ls[] = line.split("[ \t]+", 2);
if (ls.length < 2) {
System.err.printf("Unknown request: \"%s\"%n", line);
return;
}
if ("CurrentSong".equalsIgnoreCase(ls[1])) {
String current = "";
if (player != null && player.isPlaying()) {
current = player.getCurrentSongID();
}
out.printf("currentsong: %s%n", current);
} else if ("CurrentPos".equalsIgnoreCase(ls[1])) {
double[] xy = new double[] { -1, -1 };
if (player != null && player.isPlaying()) {
xy = player.getCurrentPos();
}
out.printf("currentpos: %f/%f%n", xy[0], xy[1]);
} else if ("VisTimeStamp".equalsIgnoreCase(ls[1])) {
System.err.println("VisTimeStamp not jet implemented!");
} else if ("PlayList".equalsIgnoreCase(ls[1])) {
out.println("PlayList");
if (player != null) {
for (String song : player.getPlayList()) {
out.printf("Song: %s%n", song);
}
}
out.println("EndList");
} else {
System.err.printf("Unknown request: \"%s\"%n", line);
}
}
private void receivePath(BufferedReader br) {
PNode receivedLinePath = null;
Color lineColor = Color.green;
int lineWidth = 14;
String username = socket.getInetAddress().getHostAddress();
int xSize = state.growingLayer.getXSize();
float width = state.mapPNode.getUnitWidth() * xSize;
int ySize = state.growingLayer.getYSize();
float height = state.mapPNode.getUnitHeight() * ySize;
try {
String line;
PLine currentLine = null;
while ((line = br.readLine()) != null) {
// System.out.println("-->" + line);
if (line.startsWith("LinePoint:")) {
// if (receivedLinePath == null)
if (currentLine == null) {
continue;
}
String[] lineSplit = line.split("[ /]", 3);
float currentX = Float.parseFloat(lineSplit[1]);
float currentY = Float.parseFloat(lineSplit[2]);
currentLine.addPoint(currentLine.getPointCount(), currentX * width, currentY * height);
currentLine.repaint();
try {
pathHitHist[(int) (currentX * xSize)][(int) (currentY * ySize)]++;
} catch (Exception e) {
System.out.println("X");
}
// }
} else if (line.startsWith("LineStart")) {
String[] lineSplit = line.split(" ");
for (int i = 1; i < lineSplit.length; i++) {
String[] argument = lineSplit[i].split("=", 2);
String key = argument[0];
String val = argument[1];
if (key.equals("color")) {
try {
lineColor = Color.decode(val);
} catch (Exception e) {
}
} else if (key.equals("width")) {
try {
lineWidth = Integer.parseInt(val);
} catch (NumberFormatException e) {
}
} else if (key.equals("username") || key.equals("user")) {
username = val;
}
}
receivedLinePath = utm.addPath(username, lineColor);
currentLine = new PLine();
currentLine.setStroke(new BasicStroke(lineWidth, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
currentLine.setStrokePaint(lineColor);
receivedLinePath.addChild(currentLine);
} else if (line.startsWith("LineEnd")) {
break; // Back to the start...
} else if (line.startsWith("ClearLines")) {
utm.clearPaths(username);
} else {
System.err.println("Invalid Communication:");
System.err.println(" " + line);
System.err.println("Reset.");
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void receivePlaylist(BufferedReader br) {
String receivedFileNamePrefix = "";
try {
// boolean pinChecked = false;
String line;
line = br.readLine();
if (line.startsWith("PIN: ")) {
String sentPin = line.split(" ", 2)[1];
if (sentPin.equals(pin)) {
// pinChecked = true;
authenticated = true;
socket.getOutputStream().write("OK\n".getBytes());
} else {
socket.getOutputStream().write("Wrong PIN\n".getBytes());
}
line = br.readLine();
}
// if (pinChecked) {
if (authenticated) {
ArrayList<String> songs = new ArrayList<String>();
for (; line != null; line = br.readLine()) {
// System.out.println(line);
if (line.startsWith("BaseURL:")) {
receivedFileNamePrefix = line.split(" ", 2)[1];
} else if (line.startsWith("Song:")) {
String song = line.split(" ", 2)[1];
try {
song = URLDecoder.decode(song, "utf8");
} catch (Exception e) {
}
songs.add(song);
} else if (line.startsWith("StartPath:")) {
// nop
} else if (line.startsWith("Length:")) {
// nop
} else if (line.startsWith("EndPath")) {
break;
} else {
System.err.println("Invalid Communication:");
System.err.println(" \"" + line + "\"");
System.err.println("Reset.");
break;
}
}
// This is done in the calling run-method!
// socket.close();
// Check and create the playlist
if (state.selectionPanel instanceof PlaySOMPanel) {
PlaySOMPanel play = (PlaySOMPanel) state.selectionPanel;
play.clearList();
Unit[] map = state.growingLayer.getAllUnits();
for (String song : songs) {
Unit u = mapContains(song, map);
if (u != null) {
play.addToList(song, u);
} else {
play.addToList(song, receivedFileNamePrefix, u);
}
}
play.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, play.PLAY_ALL));
} else if (state.selectionPanel instanceof PlaySOMPlayer) {
PlaySOMPlayer play = (PlaySOMPlayer) state.selectionPanel;
synchronized (play) {
play.clearList();
Unit[] map = state.growingLayer.getAllUnits();
for (String song : songs) {
Unit u = mapContains(song, map);
if (u != null) {
play.addToList(song, u);
} else {
play.addToList(song, receivedFileNamePrefix, u);
}
}
// This is now done in playerControl(...)
// play.startPlaying();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private Unit mapContains(String song, Unit[] map) {
for (Unit u : map) {
if (u != null) {
String[] songs = u.getMappedInputNames();
if (songs != null) {
for (String song2 : songs) {
if (song.equals(song2)) {
return u;
}
}
}
}
}
return null;
}
}
}
/**
* The TableModel for Users connected to the SOMViewer
*
* @author Jakob Frank
*/
class UserTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private final String[] columnNames = { "User", "Color", "Paths", "" };
private PNode remotePaths;
private GarbageCollector gc;
private List<UserTableRow> users;
public UserTableModel() {
remotePaths = new PNode();
state.mapPNode.addChild(remotePaths);
users = new ArrayList<UserTableRow>();
gc = new GarbageCollector();
gc.start();
}
public UserTableRow getRow(int i) {
return users.get(i);
}
public void setSelected(int[] selectedRows) {
for (UserTableRow r : users) {
r.setHighlighted(false);
}
for (int i : selectedRows) {
users.get(i).setHighlighted(true);
}
}
@Override
protected void finalize() throws Throwable {
gc.shutdown();
super.finalize();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public int getRowCount() {
return users.size();
}
protected PNode addPath(String username, Color color) {
UserTableRow u = null;
synchronized (users) {
// for (int i = 0; i < users.size(); i++) {
// UserTableRow utr = (UserTableRow) users.get(i);
// if (utr.username.equalsIgnoreCase(username)) {
// u = utr;
// break;
// }
// }
if (u == null) {
u = new UserTableRow(username, color);
users.add(u);
fireTableDataChanged();
}
if (u.color.getRGB() != color.getRGB()) {
u.changeColorTo(color);
}
PNode res = u.createPath();
fireTableDataChanged();
return res;
}
}
protected PNode addPath(String username) {
UserTableRow u = null;
synchronized (users) {
for (int i = 0; i < users.size(); i++) {
UserTableRow utr = users.get(i);
if (utr.username.equalsIgnoreCase(username)) {
u = utr;
break;
}
}
if (u == null) {
return null;
}
PNode res = u.createPath();
fireTableDataChanged();
return res;
}
}
public void clearPaths(String username) {
boolean dataChanged = false;
synchronized (users) {
for (int i = 0; i < users.size(); i++) {
UserTableRow utr = users.get(i);
if (utr.username.equalsIgnoreCase(username)) {
utr.clearPaths();
dataChanged = true;
}
}
}
if (dataChanged) {
fireTableDataChanged();
}
}
public void clearPaths(int index) {
synchronized (users) {
users.get(index).clearPaths();
}
fireTableDataChanged();
}
public void clearAllPaths() {
for (int i = 0; i < users.size(); i++) {
UserTableRow utr = users.get(i);
utr.clearPaths();
}
fireTableDataChanged();
}
public void removeUser(String username) {
UserTableRow u = null;
synchronized (users) {
for (int i = 0; i < users.size(); i++) {
UserTableRow utr = users.get(i);
if (utr.username.equalsIgnoreCase(username)) {
u = utr;
break;
}
}
if (u != null) {
u.clearPaths();
users.remove(u);
fireTableDataChanged();
}
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 3;
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
final UserTableRow utr = users.get(rowIndex);
switch (columnIndex) {
case 0:
return utr.username;
case 1:
return utr.color;
case 2:
return new Integer(utr.paths);
case 3:
JButton btn = new JButton("Delete");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
UserTableModel.this.clearPaths(rowIndex);
}
});
return btn;
default:
// nop
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
default:
return String.class;
case 1:
return Color.class;
case 2:
return Integer.class;
case 3:
return JButton.class;
}
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
class UserTableRow {
private static final float HIGHLIGHT_FACTOR = 1.6f;
private String username;
private Color color;
private Date timestamp;
private int paths;
private PNode node;
private boolean highlighted = false;
public UserTableRow(String username, Color color) {
this.username = username;
this.color = color;
paths = 0;
node = new PNode();
remotePaths.addChild(node);
timestamp = new Date();
}
public void reversePaths() {
PNode newNode = new PNode();
for (int i = 0; i < node.getChildrenCount(); i++) {
PLine l = (PLine) node.getChild(i);
PLine nl = new PLine();
for (int j = 0; j < l.getPointCount(); j++) {
nl.addPoint(0, l.getPoint(j, new Point2D.Double()).getX(),
l.getPoint(j, new Point2D.Double()).getY());
}
nl.setStroke(l.getStroke());
newNode.addChild(nl);
}
node = newNode;
}
public void changeColorTo(Color newColor) {
this.color = newColor;
ListIterator<?> i = node.getChildrenIterator();
while (i.hasNext()) {
PNode n = (PNode) i.next();
if (n instanceof PPath) {
PPath p = (PPath) n;
p.setStrokePaint(newColor);
}
}
}
public void setHighlighted(boolean highlight) {
if (highlighted == highlight) {
return;
}
highlighted = highlight;
float factor = 1 / HIGHLIGHT_FACTOR;
if (highlight) {
factor = HIGHLIGHT_FACTOR;
}
ListIterator<?> i = node.getChildrenIterator();
while (i.hasNext()) {
PNode n = (PNode) i.next();
if (n instanceof PPath) {
PPath p = (PPath) n;
float width = ((BasicStroke) p.getStroke()).getLineWidth();
// System.out.printf("%f, %f --> %f%n", width, factor, width * factor);
p.setStroke(new BasicStroke(width * factor));
}
}
}
public UserTableRow(String username) {
this(username, Color.CYAN);
}
public PNode createPath() {
timestamp = new Date();
paths++;
return node;
}
public void clearPaths() {
timestamp = new Date();
node.removeAllChildren();
node.repaint();
paths = 0;
}
}
private class GarbageCollector extends Thread {
private boolean running;
private int interval;
public GarbageCollector() {
this.setName("PathGarbageCollector");
running = true;
interval = 1000;
}
@Override
public void run() {
while (running) {
Date now = new Date();
List<UserTableRow> toRemove = new ArrayList<UserTableRow>();
for (int i = 0; i < users.size(); i++) {
UserTableRow utr = users.get(i);
if (utr.timestamp.getTime() + LIFETIME * 1000 < now.getTime()) {
toRemove.add(utr);
} else if (utr.paths <= 0) {
toRemove.add(utr);
}
}
if (toRemove.size() > 0) {
for (int i = 0; i < toRemove.size(); i++) {
toRemove.get(i).clearPaths();
}
synchronized (users) {
users.removeAll(toRemove);
}
fireTableDataChanged();
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
}
public void shutdown() {
running = false;
}
}
}
}