200 lines
6.5 KiB
Java
200 lines
6.5 KiB
Java
package protocolP2P;
|
||
import protocolP2P.Payload;
|
||
import protocolP2P.RequestResponseCode;
|
||
import exception.ProtocolError;
|
||
import exception.InternalError;
|
||
import exception.SizeError;
|
||
import exception.TransmissionError;
|
||
import tools.BytesArrayTools;
|
||
import java.util.Arrays;
|
||
import java.io.UnsupportedEncodingException;
|
||
|
||
/** Representation of payload for load response.
|
||
* @author Louis Royer
|
||
* @author Flavien Haas
|
||
* @author JS Auge
|
||
* @version 1.0
|
||
*/
|
||
public class FilePart extends Payload {
|
||
private String filename;
|
||
private long totalSize;
|
||
private long offset;
|
||
private byte[] partialContent;
|
||
|
||
/** Constructor (typically used by server) with informations about file part to send as parameters.
|
||
* @param filename name of the file to send
|
||
* @param totalSize total size of the file to send
|
||
* @param offset where in the file begins the part we are sending
|
||
* @param partialContent content of the file we send
|
||
* @throws InternalError
|
||
*/
|
||
public FilePart(String filename, long totalSize, long offset, byte[] partialContent) throws InternalError {
|
||
super(RequestResponseCode.LOAD_RESPONSE);
|
||
/* asserts to help debugging */
|
||
assert totalSize >= 0 : "totalSize cannot be negative";
|
||
assert partialContent.length != 0 : "partialContent.length cannot be zero";
|
||
assert totalSize >= partialContent.length : "totalSize must be greater than partialContent.length";
|
||
assert offset >= 0 : "offset cannot be negative";
|
||
assert filename != null : "filename is required";
|
||
if (totalSize < 0 || partialContent.length == 0 || totalSize < partialContent.length
|
||
|| offset < 0 || filename == null) {
|
||
throw new InternalError();
|
||
}
|
||
this.filename = filename;
|
||
this.totalSize = totalSize;
|
||
this.offset = offset;
|
||
this.partialContent = partialContent;
|
||
}
|
||
|
||
/** Constructor (typically used by client) with datagram received as parameter.
|
||
* @param datagram the full datagram received
|
||
* @throws SizeError
|
||
* @throws InternalError
|
||
* @throws TransmissionError
|
||
*/
|
||
protected FilePart(byte[] datagram) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||
super(datagram);
|
||
/* assert to help debugging */
|
||
assert requestResponseCode == RequestResponseCode.LOAD_RESPONSE : "FilePart subclass is incompatible with this datagram, request/response code must be checked before using this constructor";
|
||
/* InternalErrorException */
|
||
if (requestResponseCode != RequestResponseCode.LOAD_RESPONSE) {
|
||
throw new InternalError();
|
||
}
|
||
setOffset(datagram); // this can throw SizeError
|
||
setTotalSize(datagram); // this can throw SizeError
|
||
setFilename(datagram); // this can throw ProtocolError, SizeError
|
||
setPartialContent(datagram); // this can throw SizeError
|
||
}
|
||
|
||
/** Returns a byte[] containing datagram with padding.
|
||
* This datagram is still incomplete and should not be send directly.
|
||
* ProtocolP2PDatagram will use this method to generate the complete datagram.
|
||
* @return datagram with padding
|
||
* @throws InternalError
|
||
*/
|
||
protected byte[] toDatagram() throws InternalError {
|
||
// compute payload size
|
||
int size = 28 + filename.length() + partialContent.length;
|
||
byte[] datagram = new byte[size]; // java initialize all to zero
|
||
// set request/response code
|
||
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||
// bits 16-31 are reserved for future use
|
||
setPayloadSize(size - 8, datagram);
|
||
// write offset to datagram (Byte 8)
|
||
BytesArrayTools.write(datagram, 8, offset);
|
||
// write totalSize to datagram (Byte 16)
|
||
BytesArrayTools.write(datagram, 16, totalSize);
|
||
// write filename’s size to datagram
|
||
BytesArrayTools.write(datagram, 24, filename.length());
|
||
// write filename to datagram
|
||
try {
|
||
byte[] bFilename = filename.getBytes("UTF-8");
|
||
int i = filename.length() + 24;
|
||
for (byte b : bFilename) {
|
||
datagram[i] = b;
|
||
i += 1;
|
||
}
|
||
// write partialContent to datagram
|
||
for (byte b: partialContent) {
|
||
datagram[i] = b;
|
||
i += 1;
|
||
}
|
||
return datagram;
|
||
} catch (UnsupportedEncodingException e) {
|
||
throw new InternalError();
|
||
}
|
||
}
|
||
|
||
/** Write from bytes 8 to 15 of datagram into offset.
|
||
* @param datagram received datagram
|
||
* @throws SizeError
|
||
*/
|
||
private void setOffset(byte[] datagram) throws SizeError {
|
||
offset = BytesArrayTools.readLong(datagram, 8);
|
||
}
|
||
/** Write from bytes 16 to 23 of datagram into totalSize.
|
||
* @param datagram received datagram
|
||
* @throws SizeError
|
||
*/
|
||
private void setTotalSize(byte[] datagram) throws SizeError {
|
||
totalSize = BytesArrayTools.readLong(datagram, 16);
|
||
}
|
||
|
||
/** Read filename’s size from bytes 24 to 27 of datagram.
|
||
* @param datagram received datagram
|
||
* @throws ProtocolError
|
||
* @throws SizeError
|
||
* @return filename’s size
|
||
*/
|
||
private int getFilenameSize(byte[] datagram) throws SizeError, ProtocolError {
|
||
int size = BytesArrayTools.readInt(datagram, 24); // this can throw SizeError
|
||
// filename size cannot be zero
|
||
if (size == 0) {
|
||
throw new ProtocolError();
|
||
}
|
||
// offset (8B) + totalSize (8B) + filenameSize (4B) = 20B
|
||
if ((20 + size) > getPayloadSize(datagram)) {
|
||
throw new ProtocolError();
|
||
}
|
||
return size;
|
||
}
|
||
|
||
/** Write filename from byte 28 to byte (28 + (filenameSize - 1)) of datagram.
|
||
* @param datagram received datagram
|
||
* @throws ProtocolError
|
||
* @throws SizeError
|
||
* @throws InternalError
|
||
*/
|
||
private void setFilename(byte[] datagram) throws ProtocolError, SizeError, InternalError {
|
||
int filenameSize = getFilenameSize(datagram); // this can throw ProtocolError or SizeError
|
||
try {
|
||
filename = new String(datagram, 28, filenameSize, "UTF-8");
|
||
} catch (UnsupportedEncodingException e) {
|
||
throw new InternalError();
|
||
}
|
||
}
|
||
|
||
/** Write partialContent from byte (28 + filenameSize) to byte (8 + payloadSize) of datagram.
|
||
* @param datagram received datagram
|
||
* @throws SizeError
|
||
* @throws ProtocolError
|
||
*/
|
||
private void setPartialContent(byte[] datagram) throws ProtocolError, SizeError {
|
||
int start = 28 + getFilenameSize(datagram); // this can throw SizeError or ProtocolError
|
||
int end = 8 + getPayloadSize(datagram); // this can throw SizeError
|
||
try {
|
||
partialContent = Arrays.copyOfRange(datagram, start, end+1);
|
||
} catch (ArrayIndexOutOfBoundsException e) {
|
||
throw new ProtocolError();
|
||
}
|
||
}
|
||
|
||
/** partialContent getter.
|
||
* @return partialcontent
|
||
*/
|
||
public byte[] getPartialContent() {
|
||
return partialContent;
|
||
}
|
||
|
||
/** filename getter.
|
||
* @return String
|
||
*/
|
||
public String getFilename() {
|
||
return filename;
|
||
}
|
||
|
||
/** offset getter.
|
||
* @return offset
|
||
*/
|
||
public long getOffset() {
|
||
return offset;
|
||
}
|
||
|
||
/** totalSize getter.
|
||
* @return totalSize
|
||
*/
|
||
public long getTotalSize() {
|
||
return totalSize;
|
||
}
|
||
}
|