

package qms.messages;
import java.io.*;
import org.w3c.dom.*;
import qms.util.*;
import java.util.*;
/**
 * Un messaggio, sia scambiato con clients sia con altri nodi
 * @author  Enrico
 */
public class Message {
    
    public static final String NAME_SPACE = "qms";
    protected String id;
    protected String body;
    protected String sender;
    protected String recipient;
    protected String queueId;
    protected String type;
    protected long creation_time_stamp;
    protected Document doc;
    protected Map     attributes;
    
    /**
     * Questo oggetto riceverà una notifica quando questo messaggio (od una sua copia) verranno recapitati a qualche nodo
     */
    protected transient MessageListener listener = null;    
    
    /**
     * Costruttore
     */
    public Message() {
        creation_time_stamp = System.currentTimeMillis();
        id = "msid"+creation_time_stamp;
        this.attributes = new HashMap();
        this.type = "unset";        
        this.recipient = "unset";
        this.sender = "unset";
        this.queueId = "unset";        
    }
    
    /**
     * Ritorna il tipo di messaggio
     */
    public String getType() {
        return type;
    }
    
    /**
     * Imposta il tipo di messaggio
     */
    public void setType(String type) {
        doc = null;
        this.type = type;
    }
    /**
     * Ottiene una attributo aggiuntivo del messaggio
     */
    public String getAttribute(String name) {
        return (String) attributes.get(name);
    }
    /**
     * Imposta una attributo aggiuntivo del messaggio
     */
    public void setAttribute(String name, String value) {        
        this.attributes.put(name,value);
    }
    /**
     * Interpreta il documento XML associato al messaggio (per la deserializzazione)
     */
    protected void parseDocument() {
        Element root = doc.getDocumentElement();            
            this.sender = root.getAttribute("sender");
            this.recipient = root.getAttribute("recipient");
            this.queueId = root.getAttribute("queueId");
            this.id = root.getAttribute("id");
            this.type = root.getAttribute("type");
            this.body = root.getAttribute("body");
            NamedNodeMap attributes = root.getAttributes();
            for (int i = 0; i < attributes.getLength(); i++) {
                Attr at = (Attr) attributes.item(i);
                String name = at.getName();
                String value = at.getValue();
                setAttribute(name,value);
            }                        
    }
    /**
     * Invia al listener una notifica sulla avvenuta consegna del messaggio e dello stato della consegna
     *@param result true se il messaggio è stato consegnato
     */
    public void notifyListener(boolean result) {
        if (listener != null) listener.notifyMessageDelivered(this, result);
    }
    /**
     * Imposta il listener, ce ne può essere solo uno alla volta
     */
    public void setListener(MessageListener listener) {
        this.listener = listener;
    }
    /**
     * Crea il documento XML associato al messaggio (per la serializzazione)
     */
    protected void createDocument() throws DOMException {
        this.doc = DOMUtils.newDocument();        
        Element message = doc.createElement("message");
        doc.appendChild(message);        
        message.setAttribute("sender",sender);        
        message.setAttribute("recipient",recipient);
        message.setAttribute("id",id);
        message.setAttribute("type",type);
        message.setAttribute("queueId",queueId);        
        message.setAttribute("body",body);        
        for (Iterator i = attributes.keySet().iterator(); i.hasNext(); ) {        
            String name = (String) i.next();
            String value = (String) attributes.get(name);
            message.setAttribute(name,value);
        }        
    }
    /**
     * Identificativo del messaggio
     */
    public String getId() {
        return id;
    }
    public Message(String from, String to, String queueid, String type, String body) {
        this();
        //Logger.log("Constructing message:"+from+" "+to+" "+queueid+" "+body);
        this.sender = from;
        this.recipient = to;
        this.body = body;   
        this.type = type;
        this.queueId = queueid;         
        this.attributes = new HashMap();
        createDocument();
    }
    public Message(Document doc) throws DOMException {
        this();
        setDocument(doc);
    }
    
    public String toString() { return id+" "+type+" From "+sender+" To "+recipient+" "+body;}
    /**
     * Ritrona una stringa che rappresenta il messaggio come XML
     */
    public String asXML() {
        try {
            ByteArrayOutputStream bao = new ByteArrayOutputStream();
            serialize(bao);
            return new String(bao.toByteArray());
        } catch (Throwable e) {
            e.printStackTrace();
            throw new IllegalStateException("Can't rapresent message as XML:"+e);
        }
    }
    public Document getDocument() throws DOMException{
        return doc;        
    }
    /**
     * Scrive il messaggio in XML
     */
    public void serialize(OutputStream os) throws IOException {                    
            update();            
            DOMUtils.serialize(doc, os);        
    }
    
    public void setDocument(Document doc) throws DOMException {
        try {                        
            this.doc = doc;
            parseDocument();
        } catch (Exception e) {
            Logger.log(e);
            throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "Impossibile interpretare il messaggio XML:"+e);
        }
    }
    
    /**
     * Getter for property body.
     * @return Value of property body.
     */
    public java.lang.String getBody() {
        return body;
    }
    
    /**
     * Setter for property body.
     * @param body New value of property body.
     */
    public void setBody(java.lang.String body) {
        doc = null;
        this.body = body;
    }
    
    /**
     * Getter for property sender.
     * @return Value of property sender.
     */
    public java.lang.String getSender() {
        return sender;
    }
    
    /**
     * Setter for property sender.
     * @param sender New value of property sender.
     */
    public void setSender(java.lang.String sender) {
        doc = null;
        this.sender = sender;
    }
        
    
    /**
     * Getter for property recipient.
     * @return Value of property recipient.
     */
    public java.lang.String getRecipient() {
        return recipient;
    }
    
    /**
     * Setter for property recipient.
     * @param recipient New value of property recipient.
     */
    public void setRecipient(java.lang.String recipient) {
        doc = null;
        this.recipient = recipient;
    }
    public long getCreationTimeStamp() {
        return creation_time_stamp;
    }
    
    /**
     * Getter for property queueId.
     * @return Value of property queueId.
     */
    public java.lang.String getQueueId() {
        return queueId;
    }
    
    /**
     * Setter for property queueId.
     * @param queueId New value of property queueId.
     */
    public void setQueueId(java.lang.String queueId) {
        this.queueId = queueId;
    }
    /**
     * Costruttore per copia.
     */        
    protected Message(Message other) {
        this.sender = other.sender;
        this.recipient = other.recipient;
        this.body = other.body;
        this.creation_time_stamp = other.creation_time_stamp;        
        this.queueId = other.queueId;
        this.id = other.id;
        this.type = other.type;
        this.doc = DOMUtils.newDocument();
        this.attributes = new HashMap(other.attributes);        
        this.doc = other.doc;
        this.listener = other.listener;
        //DOMUtils.copyTree(doc, doc.getDocumentElement(), other.doc.getDocumentElement());
    }
    /**
     * Esegue una copia esatta del messaggio (compreso l'ID)
     */
    public Message copy() {
        return new Message(this);
    }
    /**
     * Aggiorna la rappresenazione interna in XML del messaggio
     */
    public void update() {
        try {
            createDocument();
        } catch (DOMException de) {
            Logger.log(de);
            throw new IllegalStateException("Unable to update message:"+de);
        }
    }
}
