/**
* Copyright (c) 2013, Will Szumski
*
* This file is part of formicidae.
*
* formicidae 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.
*
* formicidae 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 formicidae. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
*/
package org.cowboycoders.ant.messages;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.cowboycoders.ant.defines.AntMesg;
/**
* Encapsulation of an ANT message. Contains common functionality
* for standard and extended messages.
*
* @author will
*
*/
public class Message implements Messageable {
/** Holds the ant message type **/
private MessageId id;
/**
* The variable component of an Ant message with a given message ID.
* Excludes: the sync byte, length, message ID and checksum
*/
private ArrayList<Byte> payload;
public Message() {
this(MessageId.INVALID,null);
}
private Message(MessageId id, ArrayList<Byte> payload){
if (id == null) {
id = MessageId.INVALID;
}
if ( payload == null ) {
payload = new ArrayList<Byte>();
}
this.payload = payload;
this.id = id;
}
/**
* Used to create a message with an empty payload.
* Useful when constructing messages by adding data to
* the payload in stages
* @param id the message id of the ant message
*/
protected Message(MessageId id) {
this(id,null);
}
/*
public Message(Byte id, ArrayList<Byte> payload){
this(MessageId.lookUp(id),payload);
}
public Message(Message message) {
this(message.getId(),message.getPayload());
}
*/
/**
* Gets a copy of the current {@code Message.payload}
* @return {@code payload} as {@code Arraylist<Byte>}
*/
@SuppressWarnings("unchecked")
protected ArrayList<Byte> getPayload() {
return (ArrayList<Byte>)payload.clone();
}
/**
* Sets the {@code Message.payload}
* @param payload replaces the current <code>payload</code> with
* a copy of the <code>ArrayList</code> passed in
*/
@SuppressWarnings("unchecked")
protected void setPayload(ArrayList<Byte> payload) {
this.payload = (ArrayList<Byte>)payload.clone();
}
/* (non-Javadoc)
* @see org.cowboycoders.ant.messages.MessageInterface#getId()
*/
@Override
public MessageId getId() {
return id;
}
/**
* Sets the message id of the ANT message that this class encapsulates
* @param id {@code MessageType} to set
*/
protected void setId(MessageId id){
this.id = id;
}
/**
* Clears all data from message and sets the
* id to {@code MessageId.Invalid}
*/
public void reset() {
setPayload(new ArrayList<Byte>());
setId(MessageId.INVALID);
}
/* (non-Javadoc)
* @see org.cowboycoders.ant.messages.MessageInterface#getPayloadSize()
*/
@Override
public byte getPayloadSize() {
return (byte) payload.size();
}
/* (non-Javadoc)
* @see org.cowboycoders.ant.messages.MessageInterface#encode()
*/
@Override
public byte [] encode() {
List<Byte >payload = getPayloadToSend();
return getArrayFromPayload(payload);
}
/* (non-Javadoc)
* @see org.cowboycoders.ant.messages.MessageInterface#getPayloadToSend()
*/
@Override
public List<Byte> getPayloadToSend() {
return getPayload();
}
/* (non-Javadoc)
* @see org.cowboycoders.ant.messages.MessageInterface#getStandardPayload()
*/
@Override
public ArrayList<Byte> getStandardPayload() {
return getPayload();
}
/* (non-Javadoc)
* @see org.cowboycoders.ant.messages.MessageInterface#setStandardPayload(java.util.ArrayList)
*/
@Override
public void setStandardPayload(ArrayList<Byte> payload) throws ValidationException {
setPayload(payload);
}
/*
* TODO : Manage inherit doc
*/
protected void decode(byte[] buffer, boolean noChecks) throws MessageException {
if ( !noChecks && buffer.length <= AntMesg.MESG_DATA_OFFSET ) {
throw new MessageException("too few bytes to process (message incomplete)");
}
id = MessageId.lookUp(buffer[AntMesg.MESG_ID_OFFSET]);
byte[] payload = Arrays.copyOfRange(buffer, AntMesg.MESG_DATA_OFFSET, buffer.length);
ArrayList<Byte> payLoadList = new ArrayList<Byte>();
for (Byte b : payload) {
payLoadList.add(b);
}
setPayload(payLoadList);
}
/**
* Populates the {@code Message} with data. Should only
* perform validation if {@code noChecks} is false
*
* @param buffer the ant message as an array of raw bytes
* @throws MessageException on decoding error
*/
@Override
public
final void decode(byte[] buffer) throws MessageException {
decode(buffer,false);
}
/**
* Returns a copy of the {@code Message}. This should be overridden
* in any child classes.
* @return a clone
*/
@Override
public Message clone() {
Message msg = new Message();
try {
msg.decode(this.toArray(),true);
} catch (MessageException e) {
// toArray / decode is by design reversible
throw new RuntimeException("Should never reach here");
}
return msg;
}
/**
* Helper for encode / toArray
* @param payload to convert to array
* @return payload as array
*/
private byte [] getArrayFromPayload(List<Byte> payload) {
byte payloadSize = (byte) payload.size();
byte [] rtn = new byte[payloadSize + AntMesg.MESG_HEADER_SIZE];
rtn[AntMesg.MESG_SIZE_OFFSET] = payloadSize;
rtn[AntMesg.MESG_ID_OFFSET] = id.getMessageID();
for (byte i = AntMesg.MESG_DATA_OFFSET ; i < rtn.length ; i++) {
rtn[i] = payload.get(i - AntMesg.MESG_DATA_OFFSET);
}
return rtn;
}
/**
* {@inheritDoc}
*/
public byte [] toArray() {
List<Byte >payload = getPayload();
return getArrayFromPayload(payload);
}
}