/*
* Dijjer - A Peer to Peer HTTP Cache
* Copyright (C) 2004,2005 Change.Tv, Inc
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package freenet.io.comm;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import freenet.support.Logger;
import freenet.support.Serializer;
import freenet.support.ShortBuffer;
public class MessageType {
public static final String VERSION = "$Id: MessageType.java,v 1.6 2005/08/25 17:28:19 amphibian Exp $";
private static HashMap<Integer, MessageType> _specs = new HashMap<Integer, MessageType>();
private final String _name;
private final LinkedList<String> _orderedFields = new LinkedList<String>();
private final HashMap<String, Class<?>> _fields = new HashMap<String, Class<?>>();
private final HashMap<String, Class<?>> _linkedListTypes = new HashMap<String, Class<?>>();
private final boolean internalOnly;
private final short priority;
private final boolean isLossyPacketMessage;
public MessageType(String name, short priority) {
this(name, priority, false, false);
}
public MessageType(String name, short priority, boolean internal, boolean isLossyPacketMessage) {
_name = name;
this.priority = priority;
this.isLossyPacketMessage = isLossyPacketMessage;
internalOnly = internal;
// XXX hashCode() is NOT required to be unique!
Integer id = Integer.valueOf(name.hashCode());
if (_specs.containsKey(id)) {
throw new RuntimeException("A message type by the name of " + name + " already exists!");
}
_specs.put(id, this);
}
public void unregister() {
_specs.remove(Integer.valueOf(_name.hashCode()));
}
public void addLinkedListField(String name, Class<?> parameter) {
_linkedListTypes.put(name, parameter);
addField(name, LinkedList.class);
}
public void addField(String name, Class<?> type) {
_fields.put(name, type);
_orderedFields.addLast(name);
}
public void addRoutedToNodeMessageFields() {
addField(DMT.UID, Long.class);
addField(DMT.TARGET_LOCATION, Double.class);
addField(DMT.HTL, Short.class);
addField(DMT.NODE_IDENTITY, ShortBuffer.class);
}
public boolean checkType(String fieldName, Object fieldValue) {
if (fieldValue == null) {
return false;
}
Class<?> defClass = _fields.get(fieldName);
if (defClass == null) {
throw new IllegalStateException("Cannot set field \"" + fieldName + "\" which is not defined" +
" in the message type \"" + getName() + "\".");
}
Class<?> valueClass = fieldValue.getClass();
if(defClass == valueClass) return true;
if(defClass.isAssignableFrom(valueClass)) return true;
return false;
}
public Class<?> typeOf(String field) {
return _fields.get(field);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof MessageType)) {
return false;
}
// We can only register one MessageType for each name.
// So we can do == here.
return ((MessageType) o)._name == _name;
}
@Override
public int hashCode() {
return _name.hashCode();
}
public static MessageType getSpec(Integer specID, boolean dontLog) {
MessageType id = _specs.get(specID);
if (id == null) {
if(!dontLog)
Logger.error(MessageType.class, "Unrecognised message type received (" + specID + ')');
}
return id;
}
public String getName() {
return _name;
}
public Map<String, Class<?>> getFields() {
return _fields;
}
public LinkedList<String> getOrderedFields() {
return _orderedFields;
}
public Map<String, Class<?>> getLinkedListTypes() {
return _linkedListTypes;
}
/**
* @return True if this message is internal-only.
* If this is the case, any incoming messages in UDP form of this
* spec will be silently discarded.
*/
public boolean isInternalOnly() {
return internalOnly;
}
/** @return The default priority for the message type. Messages's don't necessarily
* use this: Message.boostPriority() can increase it for a realtime message, for
* instance. */
public short getDefaultPriority() {
return priority;
}
/** Only works for simple messages!! */
public int getMaxSize(int maxStringLength) {
// This method mirrors Message.encodeToPacket.
int length = 0;
length += 4; // _spec.getName().hashCode()
for (Map.Entry<String, Class<?>> entry : _fields.entrySet()) {
length += Serializer.length(entry.getValue(), maxStringLength);
}
return length;
}
public boolean isLossyPacketMessage() {
return isLossyPacketMessage;
}
}