/*
This file is part of Fantom.
Fantom is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fantom 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fantom. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.matfyz.aai.fantom.message;
import java.util.ArrayList;
import java.util.Properties;
import cz.matfyz.aai.fantom.game.Actor;
import cz.matfyz.aai.fantom.game.Graph;
import cz.matfyz.aai.fantom.game.TransportType;
import cz.matfyz.aai.fantom.game.Graph.Node;
import cz.matfyz.aai.fantom.server.ProtocolException;
/**
* Base class for messages informing about movements ('move' and 'update')
*/
public abstract class MessageMoveBase extends Message {
/**
* The name of the property that contains ID of the actor.
*/
public static final String PROPERTY_ACTOR = "actor";
/**
* The name of the property that contains the ID of the target node.
*/
public static final String PROPERTY_TO_NODE = "to";
/**
* The name of the property that contains the type of transport used
* for the movement.
*/
public static final String PROPERTY_TRANSPORT_TYPE = "transport";
/**
* Contains specification of the movement of a single actor.
*/
public static class ActorMove {
/**
* The moved actor.
*/
private Actor actor;
/**
* The target node for the actor.
*/
private Node targetNode;
/**
* The transport type used in the movement.
*/
private TransportType transportType;
/**
* Returns the moved actor.
* @return the moved actor.
*/
public Actor getActor() {
return this.actor;
}
/**
* Returns the target node.
* @return the target node.
*/
public Node getTargetNode() {
return this.targetNode;
}
/**
* Returns the transport type used for the movement.
* @return the transport type used for the movement.
*/
public TransportType getTransportType() {
return this.transportType;
}
/**
* Loads the properties of the movement from serialized data, and matches
* it to the objects in the given graph.
*
* @param graph the graph, to which the movement is matched.
* @param properties the serialized data of the movement.
* @throws ProtocolException if there is a problem with loading the movement.
*/
protected void loadProperties(Graph graph, Properties properties) throws ProtocolException {
if(graph == null)
throw new IllegalArgumentException("graph must not be null");
if(properties == null)
throw new IllegalArgumentException("properties must not be null");
String actorId = properties.getProperty(PROPERTY_ACTOR);
if(actorId == null || actorId.isEmpty())
throw new ProtocolException("No actor ID was specified", null);
this.actor = graph.getActor(actorId);
if(this.actor == null)
throw new ProtocolException("The actor '" + actorId + "' does not exist", null);
String targetId = properties.getProperty(PROPERTY_TO_NODE);
if(targetId != null && !targetId.isEmpty()) {
this.targetNode = graph.getNode(targetId);
if(this.targetNode == null)
throw new ProtocolException("The node '" + targetId + "' does not exist", null);
}
String transportName = properties.getProperty(PROPERTY_TRANSPORT_TYPE);
if(transportName != null && !transportName.isEmpty()) {
this.transportType = graph.getTransportType(transportName);
if(this.transportType == null)
throw new ProtocolException("The transport type '" + transportName + "' does not exist", null);
}
else if(targetNode == null)
throw new ProtocolException("Neither transport type nor target node was not specified", null);
}
/**
* Serializes the information about the movement.
* @return the serialized information about the movement.
*/
public Properties serialize() {
Properties res = new Properties();
res.setProperty(PROPERTY_ACTOR, actor.getId());
if(targetNode != null)
res.setProperty(PROPERTY_TO_NODE, targetNode.getId());
if(transportType != null)
res.setProperty(PROPERTY_TRANSPORT_TYPE, transportType.getName());
return res;
}
/**
* Creates a new actor movement object.
*
* @param actor the actor that performs the movement.
* @param target the target of the movement.
* @param transport the type of transport used for the movement.
*/
public ActorMove(Actor actor, Node target, TransportType transport) {
if(actor == null)
throw new IllegalArgumentException("actor must not be null");
if(transport == null && target == null)
throw new IllegalArgumentException("either transport or target must not be null");
this.actor = actor;
this.targetNode = target;
this.transportType = transport;
}
/**
* Creates a new actor movement object and loads it from properties.
*
* @param graph the game graph, for which the move is loaded.
* @param properties the serialized data of the movement.
* @throws ProtocolException if there is a problem with loading the
* movement.
*/
public ActorMove(Graph graph, Properties properties) throws ProtocolException {
loadProperties(graph, properties);
}
}
/**
* The list of moves contained in this message.
*/
private ActorMove[] moves;
/**
* Contains the raw message data.
*/
private Properties[] messageData;
/**
* Returns the specifications of moves sent in the message.
*
* @return the specifications of moves sent in the message.
*/
public ActorMove[] getMoves() {
return this.moves;
}
/**
* Loads the serialized data of the message.
*
* @param graph the graph, for which the data are loaded.
* @param messageData the serialized message data.
* @throws ProtocolException if there is a problem with loading the
* data of the message.
*/
protected void loadProperties(Graph graph, Properties[] messageData) throws ProtocolException {
ArrayList<ActorMove> actorMoves = new ArrayList<ActorMove>();
for(int i = 0; i < messageData.length; i++) {
if(messageData[i].containsKey(PROPERTY_ACTOR)) {
actorMoves.add(new ActorMove(graph, messageData[i]));
}
}
moves = actorMoves.toArray(new ActorMove[actorMoves.size()]);
}
@Override
public Properties[] serialize() {
if(messageData == null) {
assert moves != null : "Both messageData and moves is null";
messageData = new Properties[moves.length + 1];
for(int i = 0; i < moves.length; i++)
messageData[i+1] = moves[i].serialize();
Properties header = new Properties();
header.setProperty(PROPERTY_MESSAGE_TYPE, getMessageType());
messageData[0] = header;
}
return messageData;
}
/**
* Initializes a new instance of the message from serialized data.
*
* @param graph the game graph, for which the message is loaded.
* @param messageData the data of the message.
* @throws ProtocolException if there is a problem with loading the
* data of the message.
*/
public MessageMoveBase(Graph graph, Properties[] messageData) throws ProtocolException {
initialize();
loadProperties(graph, messageData);
this.messageData = messageData;
}
/**
* Initializes a new instance of the message for the given moves.
*
* @param moves the moves, for which the message is initialized.
*/
public MessageMoveBase(ActorMove[] moves) {
initialize();
if(moves == null)
throw new IllegalArgumentException("moves must not be null");
for(int i = 0; i < moves.length; i++) {
if(moves[i] == null)
throw new IllegalArgumentException("moves must not contain null values");
}
this.moves = moves;
}
/**
* Performs initialization of inner structures of the message. Derived
* classes should override this method.
*/
protected void initialize() {
}
}