forked from Cockadoodle/KOProject
com.homework.test
This commit is contained in:
parent
3a09623f92
commit
8496ff83ef
|
@ -0,0 +1,138 @@
|
|||
package com.collibra.server;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Every time a new client connects to the server a new thread is created for the client's session.
|
||||
* The session stays active until the client times out or the client sends a disconnect message.
|
||||
* @version 1.0
|
||||
* @author Yordan Kirov
|
||||
* @since 21/11/2018
|
||||
*/
|
||||
class ClientSessionThread extends Thread {
|
||||
|
||||
/** Socket used for communication between the server and the client. */
|
||||
private final Socket clientSocket;
|
||||
|
||||
/** The time when the client has connected. */
|
||||
private long startTime;
|
||||
|
||||
/** The name of the client that is retrieved after the initial communication. */
|
||||
private String clientName = null;
|
||||
|
||||
/**
|
||||
* Indicator whether the client session is still active or not.
|
||||
* When it is made inactive the client gets disconnected.
|
||||
*/
|
||||
private boolean isSessionActive;
|
||||
|
||||
/** Universally unique identifier of the client session. */
|
||||
private final String sessionId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Initializes the start time of the connection.
|
||||
* Sets the session to active.
|
||||
* Generates a universally unique identifier of the client session.
|
||||
* @param clientSocket the socket on which the client connects to the server.
|
||||
*/
|
||||
public ClientSessionThread(Socket clientSocket) {
|
||||
System.out.println("Client connected.");
|
||||
this.clientSocket = clientSocket;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.isSessionActive = true;
|
||||
this.sessionId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the communication between the server and the client.
|
||||
* Sends a first message to the client.
|
||||
* Sets the client name if the client sends it properly or waits to receive it.
|
||||
* Takes all commands of the client and answers to them.
|
||||
* When an error occurs the connection with the client is closed.
|
||||
*/
|
||||
public void run() {
|
||||
PrintWriter out = null;
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
out = new PrintWriter(clientSocket.getOutputStream(), true);
|
||||
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||
|
||||
SimpleProtocol.sendFirstMessage(out, sessionId);
|
||||
|
||||
String inputLine;
|
||||
while (isSessionActive && (inputLine = in.readLine()) != null) {
|
||||
if (this.clientName == null) {
|
||||
this.clientName = SimpleProtocol.retrieveClientName(out, inputLine);
|
||||
} else {
|
||||
acceptMessage(inputLine, out);
|
||||
}
|
||||
}
|
||||
} catch (SocketTimeoutException se) {
|
||||
System.out.println(this.clientName + " TIMED OUT!!!");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
SimpleProtocol.sendGoodbyeMessage(out, this.clientName, startTime);
|
||||
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts client messages and executes the commands according to the SimpleProtocol.
|
||||
* @param clientMessage message coming from the client
|
||||
* @param out the PrintWriter with which the server sends messages to the client.
|
||||
*/
|
||||
private void acceptMessage(String clientMessage, PrintWriter out) {
|
||||
if ("BYE MATE!".equals(clientMessage)) {
|
||||
this.isSessionActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
String serverMessage;
|
||||
if (clientMessage.startsWith("ADD NODE ")) {
|
||||
serverMessage = SimpleProtocol.addNode(clientMessage);
|
||||
} else if (clientMessage.startsWith("ADD EDGE ")) {
|
||||
serverMessage = SimpleProtocol.addEdge(clientMessage);
|
||||
} else if (clientMessage.startsWith("REMOVE NODE ")) {
|
||||
serverMessage = SimpleProtocol.removeNode(clientMessage);
|
||||
} else if (clientMessage.startsWith("REMOVE EDGE ")) {
|
||||
serverMessage = SimpleProtocol.removeEdge(clientMessage);
|
||||
} else if (clientMessage.startsWith("SHORTEST PATH ")) {
|
||||
serverMessage = SimpleProtocol.getShortestPath(clientMessage);
|
||||
} else if (clientMessage.startsWith("CLOSER THAN ")) {
|
||||
serverMessage = SimpleProtocol.getCloserThan(clientMessage);
|
||||
} else {
|
||||
serverMessage = SimpleProtocol.unsupportedCommand();
|
||||
}
|
||||
|
||||
debugToConsole(clientMessage, serverMessage);
|
||||
|
||||
out.println(serverMessage);
|
||||
}
|
||||
|
||||
private void debugToConsole(String clientMessage, String serverMessage) {
|
||||
if (Server.DEBUG) {
|
||||
System.out.println(">>>" + clientMessage);
|
||||
System.out.println(serverMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
System.out.println("Closing the connection: " + this.clientName);
|
||||
|
||||
try {
|
||||
if (clientSocket != null) {
|
||||
clientSocket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
package com.collibra.server;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Directed weighted graph, to which nodes and edges can be added, and removed asynchronically.
|
||||
* Some calculations can be made like getting the shortest path and retrieving all the nodes
|
||||
* that are closer to node X than the given weight.
|
||||
* For the calculations an implementation of the algorithm of Dijkstra with a priority queue is used.
|
||||
*
|
||||
* @param <Node> The type of the nodes in the graph.
|
||||
* @version 1.0
|
||||
* @author Yordan Kirov
|
||||
* @since 21/11/2018
|
||||
*/
|
||||
class Graph<Node> {
|
||||
/** A list of all nodes of the graph */
|
||||
private final ArrayList<Node> nodes;
|
||||
|
||||
/** A list of all edges of the graph */
|
||||
private final ArrayList<Edge> edges;
|
||||
|
||||
/** Constructor initializing the lists of nodes or edges */
|
||||
public Graph() {
|
||||
this.nodes = new ArrayList<>();
|
||||
this.edges = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given node exist in the GRAPH.
|
||||
* @param name the name of the node
|
||||
* @return Indicates whether a node exists in the GRAPH.
|
||||
*/
|
||||
public boolean containsNode(Node name) {
|
||||
return this.nodes.contains(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node to the graph.
|
||||
* The method is synchronized.
|
||||
* @param name the name of the node
|
||||
* @return Indicates whether a node exists in the GRAPH.
|
||||
*/
|
||||
public synchronized void addNode(Node name) {
|
||||
nodes.add(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node by the name of it and also removes all edges which are connected to it.
|
||||
* The method is synchronized.
|
||||
* @param name the name of the node to be removed.
|
||||
*/
|
||||
public synchronized void removeNode(Node name) {
|
||||
nodes.remove(name);
|
||||
edges.removeIf(edge -> edge.getTo().equals(name) || edge.getFrom().equals(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a directed edge to the GRAPH.
|
||||
* The method is synchronized.
|
||||
* @param from the node which is the tail of the edge.
|
||||
* @param to the node which is the head of the edge.
|
||||
* @param weight the weight of the edge. Should be a positive integer.
|
||||
*/
|
||||
public synchronized void addEdge(Node from, Node to, int weight) {
|
||||
Edge edge = new Edge(from, to, weight);
|
||||
this.edges.add(edge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all directed edges from the GRAPH which are having the same tail node and head node.
|
||||
* The method is synchronized.
|
||||
* @param from the node which is the tail of the edge.
|
||||
* @param to the node which is the head of the edge.
|
||||
*/
|
||||
public synchronized void removeEdge(String from, String to) {
|
||||
this.edges.removeIf(edge -> edge.getFrom().equals(from) && edge.getTo().equals(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the sum of the weights of the shortest path between two nodes in the GRAPH.
|
||||
* The method is synchronized.
|
||||
* @param from source node.
|
||||
* @param to target node.
|
||||
* @return the sum of the weights of the shortest path between two nodes in the GRAPH.
|
||||
* If there is no path between the nodes Integer.MAX_VALUE is returned.
|
||||
*/
|
||||
public synchronized int getShortestPathDistance(Node from, Node to) {
|
||||
Map<Node, Integer> distances = getShortestPathDistances(from);
|
||||
|
||||
return distances.get(to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all the nodes that are closer to a node from the client message than the given weight.
|
||||
*
|
||||
* For example:
|
||||
* Simple graph: Mark - 5 -> Michael - 2 -> Madeleine - 8 -> Mufasa
|
||||
* CLOSER THAN 8 Mark
|
||||
* Would return: Madeleine,Michael
|
||||
* Because Michael is at weight 5 from Mark and Madeleine is at weight 7 (5+2) from Mark.
|
||||
*
|
||||
* @param distance the max distance (sum of weight) from the source node for which the closer nodes will be listed.
|
||||
* @param source the node from which the search will start.
|
||||
* @return a comma separated list(no spaces) of found nodes, that are closer to a node from the client message,
|
||||
* sorted alphabetically by name, not including the source node.
|
||||
*/
|
||||
public synchronized String getNodesCloserThan(int distance, Node source) {
|
||||
Map<Node, Integer> distances = getShortestPathDistances(source);
|
||||
List<String> nodes = new ArrayList<>();
|
||||
for (Map.Entry<Node, Integer> nodeToDistance : distances.entrySet()) {
|
||||
if (nodeToDistance.getValue() < distance && !nodeToDistance.getKey().equals(source)) {
|
||||
nodes.add(nodeToDistance.getKey().toString());
|
||||
}
|
||||
}
|
||||
|
||||
String nodeNames = nodes.stream().sorted().collect(Collectors.joining(","));
|
||||
|
||||
return nodeNames;
|
||||
}
|
||||
|
||||
private int getSmallestWeight(Node from, Node to) {
|
||||
int weight = Integer.MAX_VALUE;
|
||||
for (Edge e : edges) {
|
||||
if (e.getFrom().equals(from) && e.getTo().equals(to)) {
|
||||
if (weight > e.getWeight()) {
|
||||
weight = e.getWeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
private List<Node> getNeighbours(Node from) {
|
||||
List<Node> neighbours = new ArrayList<>();
|
||||
for (Edge e : edges) {
|
||||
if (e.getFrom().equals(from) && !neighbours.contains(e.getTo())) {
|
||||
neighbours.add(e.getTo());
|
||||
}
|
||||
}
|
||||
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
private Map<Node, Integer> getShortestPathDistances(Node source) {
|
||||
Map<Node, Integer> distances = new HashMap<>();
|
||||
distances.put(source, 0);
|
||||
|
||||
for (Node node : nodes) {
|
||||
if (!node.equals(source)) {
|
||||
distances.put(node, Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
PriorityQueue<PrioritisedNode> queue = new PriorityQueue<>();
|
||||
queue.add(new PrioritisedNode(source, 0));
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
PrioritisedNode currentNode = queue.poll();
|
||||
|
||||
for (Node neighbour : getNeighbours(currentNode.node)) {
|
||||
int newDistance =
|
||||
distances.get(currentNode.node) + getSmallestWeight(currentNode.node, neighbour);
|
||||
|
||||
if (newDistance < distances.get(neighbour)) {
|
||||
distances.put(neighbour, newDistance);
|
||||
queue.add(new PrioritisedNode(neighbour, newDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return distances;
|
||||
}
|
||||
|
||||
private class PrioritisedNode implements Comparable<PrioritisedNode> {
|
||||
final Node node;
|
||||
final int priority;
|
||||
|
||||
PrioritisedNode(Node node, int priority) {
|
||||
this.node = node;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public int compareTo(PrioritisedNode other) {
|
||||
return Integer.compare(this.priority, other.priority);
|
||||
}
|
||||
}
|
||||
|
||||
private class Edge {
|
||||
private final Node from;
|
||||
private final Node to;
|
||||
private final int weight;
|
||||
|
||||
Edge(Node from, Node to, int weight) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
Node getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
Node getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return from + "->" + to + " " + weight;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.collibra.server;
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
|
||||
|
||||
/**
|
||||
* A simple TCP socket server that is accepting client connections in parallel, on the specified port: {@value #PORT}.
|
||||
* If client stays inactive for the time of the timeout: {@value #TIMEOUT}, it gets disconnected.
|
||||
* Debug {@value #DEBUG} is used to display the client - server communication in the console.
|
||||
* Creates a directed weighted GRAPH, that is going to be used for calculations by the clients.
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Yordan Kirov
|
||||
* @since 21/11/2018
|
||||
*/
|
||||
public class Server {
|
||||
|
||||
/** The PORT on which the server is running. Hardcoded to 50000. */
|
||||
private static final int PORT = 50000;
|
||||
|
||||
/** The timeout of every client connection to the server. Hardcoded to 30000 MS. */
|
||||
private static final int TIMEOUT = 30000;
|
||||
|
||||
/** Directed weighted graph, that is going to be used for calculations by the clients. */
|
||||
static final Graph<String> GRAPH = new Graph<>();
|
||||
|
||||
/** Debug {@value #DEBUG} is used to display the client - server communication in the console. */
|
||||
static final boolean DEBUG=false;
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("The server is listening...");
|
||||
|
||||
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
|
||||
while (true) {
|
||||
Socket socket = serverSocket.accept();
|
||||
socket.setSoTimeout(TIMEOUT);
|
||||
|
||||
ClientSessionThread st = new ClientSessionThread(socket);
|
||||
st.start();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
package com.collibra.server;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A simple protocol for exchanging messages between a server and clients.
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Yordan Kirov
|
||||
* @since 21/11/2018
|
||||
*
|
||||
*/
|
||||
class SimpleProtocol {
|
||||
/**
|
||||
* Sends a first message to every client that connects to the server.
|
||||
* @param out the PrintWriter with which the server sends messages to the client.
|
||||
* @param sessionId identification of the session with the current client.
|
||||
*/
|
||||
public static void sendFirstMessage(PrintWriter out, String sessionId) {
|
||||
out.println("HI, I'M " + sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts and validates the name of the connected client from its first message.
|
||||
*
|
||||
* The server expects a message starting with "HI, I'M " and followed by the name of the client.
|
||||
* If the client doesn't fulfil the server expectations, by sending the wrong command or sending
|
||||
* a clientName that is non alphanumeric plus the character "-", the server is awaiting for the client to give a
|
||||
* proper name and doesn't accept other commands before that.
|
||||
*
|
||||
* @param out the PrintWriter with which the sever sends messages to the client.
|
||||
* @param message received from a client.
|
||||
*
|
||||
* @return clientName - the name of the client if it is valid, otherwise returns null;
|
||||
*/
|
||||
public static String retrieveClientName(PrintWriter out, String message) {
|
||||
String clientName;
|
||||
if (message.contains("HI, I'M ")) {
|
||||
String unvalidatedClientName = message.replace("HI, I'M ", "");
|
||||
|
||||
if (isValidName(unvalidatedClientName)) {
|
||||
clientName = unvalidatedClientName;
|
||||
System.out.println("Connected client: " + clientName);
|
||||
|
||||
out.println("HI " + clientName);
|
||||
return clientName;
|
||||
}
|
||||
}
|
||||
|
||||
out.println(unsupportedCommand());
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the client before disconnecting.
|
||||
* The message includes the name of the client and the duration of the connection.
|
||||
*
|
||||
* @param out the PrintWriter with which the sever sends messages to the client.
|
||||
* @param clientName the name of the connected client.
|
||||
* @param startTime the time in milliseconds when the client connected.
|
||||
*/
|
||||
public static void sendGoodbyeMessage(PrintWriter out, String clientName, long startTime) {
|
||||
if (out != null) {
|
||||
System.out.println("GOODBYE MESSAGE!" + clientName);
|
||||
long stopTime = System.currentTimeMillis();
|
||||
long duration = stopTime - startTime;
|
||||
out.println("BYE " + clientName + ", WE SPOKE FOR " + duration + " MS");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node (as a string) to the GRAPH.
|
||||
* When the name of the node is only including alphanumeric character,
|
||||
* plus the character "-" and the node doesn't already exist.
|
||||
*
|
||||
* @param message received from a client.
|
||||
* @return returns an error message if the node already exists
|
||||
* or success message if the node has been successfully added.
|
||||
*/
|
||||
public static String addNode(String message) {
|
||||
String nodeName = message.replace("ADD NODE ", "");
|
||||
|
||||
if (!isValidName(nodeName)) {
|
||||
return unsupportedCommand();
|
||||
}
|
||||
|
||||
if (!Server.GRAPH.containsNode(nodeName)) {
|
||||
Server.GRAPH.addNode(nodeName);
|
||||
return "NODE ADDED";
|
||||
} else {
|
||||
return "ERROR: NODE ALREADY EXISTS";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a directed edge to the GRAPH.
|
||||
* A directed edge has three parts, node from which it points, node to which it points
|
||||
* and weight which is a positive integer.
|
||||
* When the names of the nodes between the edge is are already existing and
|
||||
* the format of the command is valid and the weight is a positive integer.
|
||||
*
|
||||
* @param message received from a client.
|
||||
* @return returns an error message if the the edge can't be added
|
||||
* or success message if the edge has been successfully added.
|
||||
*/
|
||||
public static String addEdge(String message) {
|
||||
String edgeString = message.replace("ADD EDGE ", "");
|
||||
String[] edgeParts = edgeString.split(" ");
|
||||
|
||||
if (!isValidEdge(edgeParts)) {
|
||||
return unsupportedCommand();
|
||||
}
|
||||
|
||||
String fromName = edgeParts[0];
|
||||
String toName = edgeParts[1];
|
||||
|
||||
if (Server.GRAPH.containsNode(fromName) && Server.GRAPH.containsNode(toName)) {
|
||||
int weight = Integer.valueOf(edgeParts[2]);
|
||||
|
||||
Server.GRAPH.addEdge(fromName, toName, weight);
|
||||
return "EDGE ADDED";
|
||||
}
|
||||
|
||||
return "ERROR: NODE NOT FOUND";
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node from the GRAPH, only when the node is already existing.
|
||||
* All edges that are linked to the removed node will also be removed.
|
||||
* @param message received from a client.
|
||||
* @return returns an error message if the node doesn't exist
|
||||
* or success message if the node has been successfully removed.
|
||||
*/
|
||||
public static String removeNode(String message) {
|
||||
String nodeName = message.replace("REMOVE NODE ", "");
|
||||
|
||||
if (Server.GRAPH.containsNode(nodeName)) {
|
||||
Server.GRAPH.removeNode(nodeName);
|
||||
return "NODE REMOVED";
|
||||
} else {
|
||||
return "ERROR: NODE NOT FOUND";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all directed edges from the GRAPH when the command is valid
|
||||
* and the both nodes that form the graph are existing.
|
||||
* @param message received from a client.
|
||||
* @return returns an error message if the command is invalid or the nodes between the graph aren't existing.
|
||||
* Returns a success message if the edges have been successfully removed.
|
||||
*/
|
||||
public static String removeEdge(String message) {
|
||||
String edgeString = message.replace("REMOVE EDGE ", "");
|
||||
String[] edgeParts = edgeString.split(" ");
|
||||
|
||||
if (edgeParts.length != 2) {
|
||||
return unsupportedCommand();
|
||||
}
|
||||
|
||||
String fromName = edgeParts[0];
|
||||
String toName = edgeParts[1];
|
||||
|
||||
if (Server.GRAPH.containsNode(fromName) && Server.GRAPH.containsNode(toName)) {
|
||||
Server.GRAPH.removeEdge(fromName, toName);
|
||||
return "EDGE REMOVED";
|
||||
}
|
||||
|
||||
return "ERROR: NODE NOT FOUND";
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the sum of the weights of the shortest path between two nodes in the GRAPH,
|
||||
* when the input of the command is valid and the nodes exist in the GRAPH.
|
||||
* @param message received from a client.
|
||||
* @return the sum of the weights of the shortest path between two nodes.
|
||||
* Returns an error message if the command is invalid or the nodes of the GRAPH aren't existing.
|
||||
*/
|
||||
public static String getShortestPath(String message) {
|
||||
String fromAndTo = message.replace("SHORTEST PATH ", "");
|
||||
String[] fromTo = fromAndTo.split(" ");
|
||||
if (fromTo.length != 2) {
|
||||
return unsupportedCommand();
|
||||
}
|
||||
|
||||
String from = fromTo[0];
|
||||
String to = fromTo[1];
|
||||
|
||||
if (Server.GRAPH.containsNode(from) && Server.GRAPH.containsNode(to)) {
|
||||
return String.valueOf(Server.GRAPH.getShortestPathDistance(from, to));
|
||||
}
|
||||
|
||||
return "ERROR: NODE NOT FOUND";
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all the nodes that are closer to a node from the client message than the given weight.
|
||||
*
|
||||
* For example:
|
||||
* Simple graph: Mark - 5 -> Michael - 2 -> Madeleine - 8 -> Mufasa
|
||||
* CLOSER THAN 8 Mark
|
||||
* Would return: Madeleine,Michael
|
||||
* Because Michael is at weight 5 from Mark and Madeleine is at weight 7 (5+2) from Mark.
|
||||
*
|
||||
* @param message received from a client.
|
||||
* @return a comma separated list(no spaces) of found nodes, that are closer to a node from the client message,
|
||||
* sorted alphabetically by name, not including the starting node.
|
||||
* Returns an error message if the command is invalid or the node of the GRAPH isn't existing.
|
||||
*/
|
||||
public static String getCloserThan(String message) {
|
||||
String weightAndNode = message.replace("CLOSER THAN ", "");
|
||||
String[] weightNode = weightAndNode.split(" ");
|
||||
|
||||
if (weightNode.length != 2 || !isValidWeightString(weightNode[0])) {
|
||||
return unsupportedCommand();
|
||||
}
|
||||
int weight = Integer.valueOf(weightNode[0]);
|
||||
String nodeName = weightNode[1];
|
||||
|
||||
if (Server.GRAPH.containsNode(nodeName)) {
|
||||
String path = Server.GRAPH.getNodesCloserThan(weight, nodeName);
|
||||
return path;
|
||||
}
|
||||
|
||||
return "ERROR: NODE NOT FOUND";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string for unsupported command.
|
||||
* @return string for unsupported command.
|
||||
*/
|
||||
public static String unsupportedCommand() {
|
||||
return "SORRY, I DIDN'T UNDERSTAND THAT";
|
||||
}
|
||||
|
||||
|
||||
private static boolean isValidEdge(String[] input) {
|
||||
if (input.length != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String weightString = input[2];
|
||||
|
||||
boolean isValidWeightString = isValidWeightString(weightString);
|
||||
|
||||
return isValidWeightString;
|
||||
}
|
||||
|
||||
private static boolean isValidWeightString(String weightString) {
|
||||
return weightString.length() > 0 &&
|
||||
weightString.chars().allMatch(Character::isDigit);
|
||||
}
|
||||
|
||||
private static boolean isValidName(String input) {
|
||||
return input.matches("[A-Za-z0-9-]+");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue