/*
* Copyright (c) 2006, KNOPFLERFISH project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* - Neither the name of the KNOPFLERFISH project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Philippe Laporte
*/
package org.knopflerfish.bundle.connectors.datagram;
import java.io.IOException;
import java.io.EOFException;
import java.io.UTFDataFormatException;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.URI;
import javax.microedition.io.Datagram;
/*
MO: This feels a bit "unclean" -- can someone think of a nicer way to do this?
*/
class DatagramImpl implements Datagram {
private DatagramPacket dgram;
private String address;
private int pos;
public DatagramImpl(byte[] buffer, int length, String address) {
dgram = new DatagramPacket(buffer, length);
if (address != null)
setAddress(address);
pos = 0;
}
public DatagramPacket getDatagramPacket() {
return dgram;
}
public String getAddress() {
return "datagram://" + dgram.getAddress().getHostAddress() + ":" + dgram.getPort();
}
public int getLength() {
return dgram.getLength();
}
public int getOffset() {
return dgram.getOffset();
}
public byte[] getData() {
return dgram.getData();
}
public void setData(byte[] buf, int off, int len) {
dgram.setData(buf, off, len);
}
public void setAddress(Datagram data) {
setAddress(data.getAddress());
}
public void setAddress(String address) {
/*
I (M-O) have compared how this function behaves against
a sun implementation of javax.microedtion.io.Datagram
and it seems to behave in the same way.
*/
if (address == null || !address.startsWith("datagram://")) {
throw new IllegalArgumentException("Excepted datagram://-address");
}
int portSep = address.lastIndexOf(':');
if (portSep < 10) // the portSep needs to lie past "datagram://"
throw new IllegalArgumentException("Missing port");
String port = null;
port = address.substring(portSep + 1, address.length());
if (!port.equals("")) {
try {
int portNo = Integer.parseInt(port);
dgram.setPort(portNo);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid port");
}
}
String host = address.substring(11, portSep); // 11 = "datagram://"
try {
dgram.setAddress(InetAddress.getByName(host));
} catch(Exception e) {
throw new IllegalArgumentException("Invalid host");
}
}
public void setLength(int len) {
dgram.setLength(len);
}
public void reset() {
byte[] b = dgram.getData();
pos = 0;
setData(b, 0, 0);
}
/*
I've tried to compare the read/write functions to a j2se Sun implementation
of the javax.microedition.io.Datagram but it did not seem to behave properly.
One couldn't even execute simple examples like (taken from description):
<code>
...
Datagram dg = con.newDatagram(100);
dg.reset();
dg.writeUTF("hej hopp i lingonskogen");
</code>
Gives exceptions.. therefor the read/write functions have not been tested
against a "real" javax.microedition.io implementation.
This is a big "TODO".
The functions have however been tested against "themselves".
*/
public void readFully(byte[] buf) throws IOException {
readFully(buf, 0, buf.length);
}
public void readFully(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
for (int i = off; i < off + len; i++) {
buf[i] = readByte();
}
}
public int skipBytes(int num) throws IOException {
byte[] b = getData();
int new_pos = Math.min(pos + num, getLength());
int change = new_pos - pos;
pos = new_pos;
return change;
}
public boolean readBoolean() throws IOException {
return readByte() != 0;
}
// returns an unsigned byte.
private int read() throws IOException {
byte[] b = getData();
if (pos > b.length)
throw new EOFException();
// Is this really correct? Should getOffset be included?
return b[getOffset() + pos++] & 0xff;
}
public byte readByte() throws IOException {
return (byte)read();
}
public int readUnsignedByte() throws IOException {
return read();
}
public short readShort() throws IOException {
return (short)(((short)readByte()) << 8 | ((short)readByte()));
}
public int readUnsignedShort() throws IOException {
return (int)readShort();
}
public char readChar() throws IOException {
return (char)readShort();
}
public int readInt() throws IOException {
return (int)(read() << 24 |
read() << 16 |
read() << 8 |
read());
}
public long readLong() throws IOException {
// Why do I need to do this? Sometimes I get 0xffffffff<the wanted pattern>. Why?
long c1 = ((read() << 24 | read() << 16 | read() << 8 | read()) & 0x00000000FFFFFFFFL);
long c2 = ((read() << 24 | read() << 16 | read() << 8 | read()) & 0x00000000FFFFFFFFL);
return (c1 << 32 | c2);
}
public String readUTF() throws IOException {
return DataInputStream.readUTF(this);
}
public String readLine() throws IOException {
StringBuffer bf = new StringBuffer();
char c;
while (true) {
c = readChar();
bf.append(c);
if (c == '\n' || c == '\r') {
break;
}
}
return bf.toString();
}
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
public void write(int val) throws IOException {
byte[] b = getData();
// is this really correct, should getOffset be added?
b[getOffset() + pos] = (byte)val; // should throw ArrayIndexOutOfBounds
pos++;
}
public void write(byte[] buf) throws IOException {
write(buf, 0, buf.length);
}
public void write(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
for (int i = off; i < off + len; i++) {
write(buf[i]);
}
}
public void writeBoolean(boolean bool) throws IOException {
write(bool ? 1 : 0);
}
public void writeByte(int b) throws IOException {
write(b);
}
public void writeShort(int sh) throws IOException {
write(0xff & (sh >>> 8));
write(sh & 0xff);
}
public void writeChar(int ch) throws IOException {
writeShort(ch);
}
public void writeInt(int val) throws IOException {
writeShort(val >>> 16);
writeShort(val & 0xffff);
}
public void writeLong(long val) throws IOException {
write((int)(val >>> 56));
write((int)(val >>> 48));
write((int)(val >>> 40));
write((int)(val >>> 32));
write((int)(val >>> 24));
write((int)(val >>> 16));
write((int)(val >>> 8));
write((int)(val ));
}
public void writeChars(String str) throws IOException {
for (int i = 0; i < str.length(); i++)
writeChar((int)str.charAt(i));
}
public void writeUTF(String str) throws IOException {
int bytes = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) >= '\u0001' && str.charAt(i) <= '\u007f') {
bytes++;
} else if (str.charAt(i) == '\u0000' ||
(str.charAt(i) >= '\u0080' && str.charAt(i) <= '\u07ff')) {
bytes += 2;
} else if (str.charAt(i) >= '\u0800' && str.charAt(i) <= '\uffff') {
bytes += 3;
}
}
if (bytes > 65535)
throw new UTFDataFormatException("String is to long");
writeShort((short)bytes);
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) >= '\u0001' && str.charAt(i) <= '\u007f') {
write(str.charAt(i));
} else if (str.charAt(i) == '\u0000' ||
(str.charAt(i) >= '\u0080' && str.charAt(i) <= '\u07ff')) {
write(0xc0 | (0x1f & (str.charAt(i) >> 6)));
write(0x80 | (0x3f & str.charAt(i)));
} else if (str.charAt(i) >= '\u0800' && str.charAt(i) <= '\u07ff') {
write(0xe0 | (0x0f & (str.charAt(i) >> 12)));
write(0x80 | (0x3f & (str.charAt(i) >> 6)));
write(0x80 | (0x3f & str.charAt(i)));
}
}
}
public void writeBytes(String str) throws IOException {
byte[] bytes = str.getBytes();
write(bytes);
}
public void writeFloat(float val) throws IOException {
writeInt((int)Float.floatToIntBits(val));
}
public void writeDouble(double val) throws IOException {
long v = Double.doubleToLongBits(val);
writeLong(v);
}
}