package thaw.fcp;
import java.util.Enumeration;
import java.util.Hashtable;
import thaw.core.Logger;
/**
* This class is a generic class, able to handle all kind of FCPMessage.
* Raw data are NOT stored inside. You *have* to handle them by yourself
* (FCPConnection.read() / FCPConnection.write())
* after reading / writing a message with this class.
*/
public class FCPMessage {
private String messageName = null;
private Hashtable fields = null; /* String (field) -> String (value) ; See http://wiki.freenetproject.org/FreenetFCPSpec2Point0 */
private long dataWaiting = 0;
public FCPMessage() {
fields = new Hashtable();
}
/**
* As you can't fetch the value returns by loadFromRawMessage(), this constructor is not recommanded.
*/
public FCPMessage(final String rawMessage) {
this();
loadFromRawMessage(rawMessage);
}
/**
* Raw message does not need to finish by "EndMessage" / "Data".
*/
public boolean loadFromRawMessage(final String rawMessage) {
int i;
final String[] lines = rawMessage.split("\n");
for(i = 0 ; "".equals( lines[i] );) {
i++;
}
setMessageName(lines[i]);
for(i++; i < lines.length ; i++) {
/* Empty lines are ignored. */
/* Line not containing '=' (like "Data" or "EndMessage") are ignored */
if("".equals( lines[i] ) || !(lines[i].indexOf("=") >= 0))
continue;
int equalPos = lines[i].indexOf('=');
String name = lines[i].substring(0, equalPos);
String value = lines[i].substring(equalPos+1);
setValue(name, value);
}
if("ProtocolError".equals( getMessageName() )
&& !"25".equals(getValue("Code")) ) /* code 25 == need to test DDA */
Logger.warning(this, "PROTOCOL ERROR:\n"+toString());
else if (Logger.getLogLevel() <= 3)
Logger.info(this, "Message (Node >> Thaw): "+getMessageName());
else
Logger.debug(this, "Message (Node >> Thaw): "+toString());
return true;
}
public String getMessageName() {
return messageName;
}
public void setMessageName(final String name) {
if ((name == null) || "".equals( name )) {
Logger.notice(this, "Setting name to empty ? weird");
}
if (name.indexOf("\n") >= 0) {
Logger.notice(this, "Name shouldn't contain '\n'");
}
messageName = name;
}
public String getValue(final String field) {
return ((String)fields.get(field));
}
public Hashtable getValues() {
return fields;
}
public void setValue(final String field, final String value) {
if("DataLength".equals( field )) {
setAmountOfDataWaiting((new Long(value)).longValue());
}
if(value == null) {
fields.remove(field);
return;
}
fields.put(field, value);
}
/**
* Returns the amount of data waiting on socket (in octets).
* @return if > 0 : Data are still waiting (except if the message name is "PersistentPut" !), if == 0 : No data waiting, if < 0 : These data are now unavailable.
*/
public long getAmountOfDataWaiting() {
return dataWaiting;
}
public void setAmountOfDataWaiting(final long amount) {
if(amount == 0) {
Logger.warning(this, "Setting amount of data waiting to 0 ?! Abnormal !");
}
dataWaiting = amount;
}
/**
* Generate FCP String to send.
* If amount of data waiting is set to > 0, then, a field "DataLength" is added,
* and resulting string finish by "Data", else resulting string simply finish by "EndMessage".
*/
public String toString() {
String result = "";
Logger.info(this, "Message (Node << Thaw): "+getMessageName());
result = result + getMessageName() + "\n";
for(final Enumeration fieldNames = fields.keys() ; fieldNames.hasMoreElements();) {
final String fieldName = ((String)fieldNames.nextElement());
result = result + fieldName + "=" + getValue(fieldName) + "\n";
}
if(getAmountOfDataWaiting() == 0)
result = result + "EndMessage\n";
else {
result = result + "DataLength="+ (new Long(getAmountOfDataWaiting())).toString() + "\n";
result = result + "Data\n";
}
return result;
}
}