/**
* ***************************************************************
* JADE - Java Agent DEvelopment Framework is a framework to develop
* multi-agent systems in compliance with the FIPA specifications.
* Copyright (C) 2000 CSELT S.p.A.
*
* GNU Lesser General Public License
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation,
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* **************************************************************
*/
package jade.content.frame;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Date;
import java.io.*;
import jade.core.AID;
import jade.content.lang.sl.SL0Vocabulary;
import jade.util.leap.Iterator;
/**
@author Giovanni Caire - TILAB
*/
public class LEAPFrameCodec implements jade.util.leap.Serializable {
public static final String NAME = "LEAP";
private transient ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
private transient DataOutputStream outStream = new DataOutputStream(outBuffer);
private transient Vector stringReferences = new Vector();
//#MIDP_EXCLUDE_BEGIN
private void readObject(java.io.ObjectInputStream oin) throws java.io.IOException, ClassNotFoundException {
oin.defaultReadObject();
outBuffer = new ByteArrayOutputStream();
outStream = new DataOutputStream(outBuffer);
stringReferences = new Vector();
}
//#MIDP_EXCLUDE_END
// Primitive types
private static final byte STRING = 6;
private static final byte BOOLEAN = 7;
private static final byte INTEGER = 8;
private static final byte FLOAT = 9;
private static final byte DATE = 10;
private static final byte BYTE_SEQUENCE = 11;
// Structured types
private static final byte AGGREGATE = 1;
private static final byte CONTENT_ELEMENT_LIST = 2;
private static final byte OBJECT = 3;
// Markers for structured types
private static final byte ELEMENT = 4;
private static final byte END = 5;
// Modifiers for string encoding
private static final byte MODIFIER = (byte) 0x10; // Only bit five set to 1
private static final byte UNMODIFIER = (byte) 0xEF; // Only bit five cleared to 1
/**
Transform a Frame into a sequence of bytes encoded according to the
LEAP language
@param content The Frame to be transformed
@throws FrameException
*/
public synchronized byte[] encode(Frame content) throws FrameException {
if (content == null) {
return null;
}
try {
write(outStream, content);
return outBuffer.toByteArray();
}
catch (FrameException fe) {
throw fe;
}
catch (Throwable t) {
throw new FrameException("Error encoding content", t);
}
finally {
outBuffer.reset();
stringReferences.removeAllElements();
}
}
/**
Transform a sequence of bytes encoded according to the LEAP
language into a Frame
@param content The sequence of bytes to be transformed.
@throws FrameException
*/
public synchronized Frame decode(byte[] content) throws FrameException {
if (content == null || content.length == 0) {
return null;
}
ByteArrayInputStream inpBuffer = null;
DataInputStream inpStream = null;
try {
inpBuffer = new ByteArrayInputStream(content);
inpStream = new DataInputStream(inpBuffer);
return (Frame) read(inpStream);
}
catch (FrameException fe) {
//fe.printStackTrace();
throw fe;
}
catch (Throwable t) {
//t.printStackTrace();
throw new FrameException("Error decoding content", t);
}
finally {
try {
inpStream.close();
inpBuffer.close();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
stringReferences.removeAllElements();
}
}
/**
*/
private void write(DataOutputStream stream, Object obj) throws Throwable {
// PRIMITIVES
if (obj instanceof String) {
writeString(stream, STRING, (String) obj);
}
else if (obj instanceof Boolean) {
stream.writeByte(BOOLEAN);
stream.writeBoolean(((Boolean) obj).booleanValue());
}
else if (obj instanceof Long) {
stream.writeByte(INTEGER);
stream.writeLong(((Long) obj).longValue());
}
//#MIDP_EXCLUDE_BEGIN
else if (obj instanceof Double) {
stream.writeByte(FLOAT);
stream.writeDouble(((Double) obj).doubleValue());
}
//#MIDP_EXCLUDE_END
else if (obj instanceof Date) {
stream.writeByte(DATE);
stream.writeLong(((Date) obj).getTime());
}
else if (obj instanceof byte[]) {
stream.writeByte(BYTE_SEQUENCE);
byte[] b = (byte[]) obj;
stream.writeInt(b.length);
stream.write(b, 0, b.length);
}
// ORDERED FRAME
else if (obj instanceof OrderedFrame) {
OrderedFrame f = (OrderedFrame) obj;
int size = f.size();
String typeName = f.getTypeName();
if (typeName != null) {
// AGGREGATE
writeString(stream, AGGREGATE, typeName);
}
else {
// CONTENT_ELEMENT_LIST
stream.writeByte(CONTENT_ELEMENT_LIST);
}
for (int i = 0; i < size; i++) {
stream.writeByte(ELEMENT);
write(stream, f.elementAt(i));
}
stream.writeByte(END);
}
// QUALIFIED_FRAME
else if (obj instanceof QualifiedFrame) {
QualifiedFrame f = (QualifiedFrame) obj;
writeString(stream, OBJECT, f.getTypeName());
Enumeration e = f.keys();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
writeString(stream, ELEMENT, key);
write(stream, f.get(key));
}
stream.writeByte(END);
}
// AID
else if (obj instanceof AID) {
// Convert the AID into a qualified frame and call write() again
write(stream, aidToFrame((AID) obj));
}
else {
throw new FrameException("Object "+obj+" cannot be encoded");
}
}
/**
*/
private Object read(DataInputStream stream) throws Throwable {
Object obj = null;
byte type = stream.readByte();
// PRIMITIVES
if ((type&UNMODIFIER) == STRING) {
obj = readString(stream, type);
}
else if (type == BOOLEAN) {
obj = new Boolean(stream.readBoolean());
}
else if (type == INTEGER) {
obj = new Long(stream.readLong());
}
//#MIDP_EXCLUDE_BEGIN
else if (type == FLOAT) {
obj = new Double(stream.readDouble());
}
//#MIDP_EXCLUDE_END
else if (type == DATE) {
obj = new Date(stream.readLong());
}
else if (type == BYTE_SEQUENCE) {
int length = stream.readInt();
obj = new byte[length];
stream.read((byte[]) obj, 0, length);
}
// AGGREGATE
else if ((type&UNMODIFIER) == AGGREGATE) {
String typeName = readString(stream, type);
OrderedFrame f = new OrderedFrame(typeName);
fillOrderedFrame(stream, f);
obj = f;
}
// CONTENT_ELEMENT_LIST
else if (type == CONTENT_ELEMENT_LIST) {
OrderedFrame f = new OrderedFrame(null);
fillOrderedFrame(stream, f);
obj = f;
}
// QUALIFIED_FRAME
else if ((type&UNMODIFIER) == OBJECT) {
String typeName = readString(stream, type);
QualifiedFrame f = new QualifiedFrame(typeName);
byte marker = stream.readByte();
do {
if ((marker&UNMODIFIER) == ELEMENT) {
String elementName = readString(stream, marker);
Object elementVal = read(stream);
f.put(elementName, elementVal);
marker = stream.readByte();
}
}
while (marker != END);
// If this QualifiedFrame represents an AID, convert it
if (f.getTypeName().equals(SL0Vocabulary.AID)) {
obj = frameToAid(f);
}
else {
obj = f;
}
}
else {
throw new FrameException("Unexpected tag "+type);
}
return obj;
}
private void fillOrderedFrame(DataInputStream stream, OrderedFrame f) throws Throwable {
byte marker = stream.readByte();
do {
if (marker == ELEMENT) {
Object elementVal = read(stream);
f.addElement(elementVal);
marker = stream.readByte();
}
}
while (marker != END);
}
private final void writeString(DataOutputStream stream, byte tag, String s) throws Throwable {
int index = stringReferences.indexOf(s);
if (index >= 0) {
// Write the tag modified and just put the index
stream.writeByte(tag|MODIFIER);
stream.writeByte(index);
}
else {
stream.writeByte(tag);
stream.writeUTF(s);
if ((s.length() > 1) && (stringReferences.size() < 256)) {
stringReferences.addElement(s);
//System.out.println("writeString: added:"+s);
}
}
}
private final String readString(DataInputStream stream, byte tag) throws Throwable {
if ((tag&MODIFIER) != 0) {
int index = stream.readUnsignedByte();
String s = (String) stringReferences.elementAt(index);
return s;
}
else {
String s = stream.readUTF();
if ((s.length() > 1) && (stringReferences.size() < 256)) {
stringReferences.addElement(s);
}
return s;
}
}
private final QualifiedFrame aidToFrame(AID id) {
QualifiedFrame f = new QualifiedFrame(SL0Vocabulary.AID);
// Name
f.put(SL0Vocabulary.AID_NAME, id.getName());
// Addresses
Iterator i = id.getAllAddresses();
if (i.hasNext()) {
OrderedFrame addresses = new OrderedFrame(SL0Vocabulary.SEQUENCE);
while (i.hasNext()) {
addresses.addElement(i.next());
}
f.put(SL0Vocabulary.AID_ADDRESSES, addresses);
}
// Resolvers
i = id.getAllResolvers();
if (i.hasNext()) {
OrderedFrame resolvers = new OrderedFrame(SL0Vocabulary.SEQUENCE);
while (i.hasNext()) {
AID res = (AID) i.next();
resolvers.addElement(aidToFrame(res));
}
f.put(SL0Vocabulary.AID_RESOLVERS, resolvers);
}
return f;
}
private final AID frameToAid(QualifiedFrame f) {
// Name
AID id = new AID((String) f.get(SL0Vocabulary.AID_NAME), AID.ISGUID);
// Addresses
OrderedFrame addresses = (OrderedFrame) f.get(SL0Vocabulary.AID_ADDRESSES);
if (addresses != null) {
for (int i = 0; i < addresses.size(); ++i) {
id.addAddresses((String) addresses.elementAt(i));
}
}
// Resolvers
OrderedFrame resolvers = (OrderedFrame) f.get(SL0Vocabulary.AID_RESOLVERS);
if (resolvers != null) {
for (int i = 0; i < resolvers.size(); ++i) {
AID res = frameToAid((QualifiedFrame) resolvers.elementAt(i));
id.addResolvers(res);
}
}
return id;
}
}