| /** |
| * $RCSfile$ |
| * $Revision$ |
| * $Date$ |
| * |
| * Copyright 2003-2007 Jive Software. |
| * |
| * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.jivesoftware.smack.packet; |
| |
| import org.jivesoftware.smack.util.StringUtils; |
| |
| import java.util.*; |
| |
| /** |
| * Represents XMPP message packets. A message can be one of several types: |
| * |
| * <ul> |
| * <li>Message.Type.NORMAL -- (Default) a normal text message used in email like interface. |
| * <li>Message.Type.CHAT -- a typically short text message used in line-by-line chat interfaces. |
| * <li>Message.Type.GROUP_CHAT -- a chat message sent to a groupchat server for group chats. |
| * <li>Message.Type.HEADLINE -- a text message to be displayed in scrolling marquee displays. |
| * <li>Message.Type.ERROR -- indicates a messaging error. |
| * </ul> |
| * |
| * For each message type, different message fields are typically used as follows: |
| * <p> |
| * <table border="1"> |
| * <tr><td> </td><td colspan="5"><b>Message type</b></td></tr> |
| * <tr><td><i>Field</i></td><td><b>Normal</b></td><td><b>Chat</b></td><td><b>Group Chat</b></td><td><b>Headline</b></td><td><b>XMPPError</b></td></tr> |
| * <tr><td><i>subject</i></td> <td>SHOULD</td><td>SHOULD NOT</td><td>SHOULD NOT</td><td>SHOULD NOT</td><td>SHOULD NOT</td></tr> |
| * <tr><td><i>thread</i></td> <td>OPTIONAL</td><td>SHOULD</td><td>OPTIONAL</td><td>OPTIONAL</td><td>SHOULD NOT</td></tr> |
| * <tr><td><i>body</i></td> <td>SHOULD</td><td>SHOULD</td><td>SHOULD</td><td>SHOULD</td><td>SHOULD NOT</td></tr> |
| * <tr><td><i>error</i></td> <td>MUST NOT</td><td>MUST NOT</td><td>MUST NOT</td><td>MUST NOT</td><td>MUST</td></tr> |
| * </table> |
| * |
| * @author Matt Tucker |
| */ |
| public class Message extends Packet { |
| |
| private Type type = Type.normal; |
| private String thread = null; |
| private String language; |
| |
| private final Set<Subject> subjects = new HashSet<Subject>(); |
| private final Set<Body> bodies = new HashSet<Body>(); |
| |
| /** |
| * Creates a new, "normal" message. |
| */ |
| public Message() { |
| } |
| |
| /** |
| * Creates a new "normal" message to the specified recipient. |
| * |
| * @param to the recipient of the message. |
| */ |
| public Message(String to) { |
| setTo(to); |
| } |
| |
| /** |
| * Creates a new message of the specified type to a recipient. |
| * |
| * @param to the user to send the message to. |
| * @param type the message type. |
| */ |
| public Message(String to, Type type) { |
| setTo(to); |
| this.type = type; |
| } |
| |
| /** |
| * Returns the type of the message. If no type has been set this method will return {@link |
| * org.jivesoftware.smack.packet.Message.Type#normal}. |
| * |
| * @return the type of the message. |
| */ |
| public Type getType() { |
| return type; |
| } |
| |
| /** |
| * Sets the type of the message. |
| * |
| * @param type the type of the message. |
| * @throws IllegalArgumentException if null is passed in as the type |
| */ |
| public void setType(Type type) { |
| if (type == null) { |
| throw new IllegalArgumentException("Type cannot be null."); |
| } |
| this.type = type; |
| } |
| |
| /** |
| * Returns the default subject of the message, or null if the subject has not been set. |
| * The subject is a short description of message contents. |
| * <p> |
| * The default subject of a message is the subject that corresponds to the message's language. |
| * (see {@link #getLanguage()}) or if no language is set to the applications default |
| * language (see {@link Packet#getDefaultLanguage()}). |
| * |
| * @return the subject of the message. |
| */ |
| public String getSubject() { |
| return getSubject(null); |
| } |
| |
| /** |
| * Returns the subject corresponding to the language. If the language is null, the method result |
| * will be the same as {@link #getSubject()}. Null will be returned if the language does not have |
| * a corresponding subject. |
| * |
| * @param language the language of the subject to return. |
| * @return the subject related to the passed in language. |
| */ |
| public String getSubject(String language) { |
| Subject subject = getMessageSubject(language); |
| return subject == null ? null : subject.subject; |
| } |
| |
| private Subject getMessageSubject(String language) { |
| language = determineLanguage(language); |
| for (Subject subject : subjects) { |
| if (language.equals(subject.language)) { |
| return subject; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a set of all subjects in this Message, including the default message subject accessible |
| * from {@link #getSubject()}. |
| * |
| * @return a collection of all subjects in this message. |
| */ |
| public Collection<Subject> getSubjects() { |
| return Collections.unmodifiableCollection(subjects); |
| } |
| |
| /** |
| * Sets the subject of the message. The subject is a short description of |
| * message contents. |
| * |
| * @param subject the subject of the message. |
| */ |
| public void setSubject(String subject) { |
| if (subject == null) { |
| removeSubject(""); // use empty string because #removeSubject(null) is ambiguous |
| return; |
| } |
| addSubject(null, subject); |
| } |
| |
| /** |
| * Adds a subject with a corresponding language. |
| * |
| * @param language the language of the subject being added. |
| * @param subject the subject being added to the message. |
| * @return the new {@link org.jivesoftware.smack.packet.Message.Subject} |
| * @throws NullPointerException if the subject is null, a null pointer exception is thrown |
| */ |
| public Subject addSubject(String language, String subject) { |
| language = determineLanguage(language); |
| Subject messageSubject = new Subject(language, subject); |
| subjects.add(messageSubject); |
| return messageSubject; |
| } |
| |
| /** |
| * Removes the subject with the given language from the message. |
| * |
| * @param language the language of the subject which is to be removed |
| * @return true if a subject was removed and false if it was not. |
| */ |
| public boolean removeSubject(String language) { |
| language = determineLanguage(language); |
| for (Subject subject : subjects) { |
| if (language.equals(subject.language)) { |
| return subjects.remove(subject); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Removes the subject from the message and returns true if the subject was removed. |
| * |
| * @param subject the subject being removed from the message. |
| * @return true if the subject was successfully removed and false if it was not. |
| */ |
| public boolean removeSubject(Subject subject) { |
| return subjects.remove(subject); |
| } |
| |
| /** |
| * Returns all the languages being used for the subjects, not including the default subject. |
| * |
| * @return the languages being used for the subjects. |
| */ |
| public Collection<String> getSubjectLanguages() { |
| Subject defaultSubject = getMessageSubject(null); |
| List<String> languages = new ArrayList<String>(); |
| for (Subject subject : subjects) { |
| if (!subject.equals(defaultSubject)) { |
| languages.add(subject.language); |
| } |
| } |
| return Collections.unmodifiableCollection(languages); |
| } |
| |
| /** |
| * Returns the default body of the message, or null if the body has not been set. The body |
| * is the main message contents. |
| * <p> |
| * The default body of a message is the body that corresponds to the message's language. |
| * (see {@link #getLanguage()}) or if no language is set to the applications default |
| * language (see {@link Packet#getDefaultLanguage()}). |
| * |
| * @return the body of the message. |
| */ |
| public String getBody() { |
| return getBody(null); |
| } |
| |
| /** |
| * Returns the body corresponding to the language. If the language is null, the method result |
| * will be the same as {@link #getBody()}. Null will be returned if the language does not have |
| * a corresponding body. |
| * |
| * @param language the language of the body to return. |
| * @return the body related to the passed in language. |
| * @since 3.0.2 |
| */ |
| public String getBody(String language) { |
| Body body = getMessageBody(language); |
| return body == null ? null : body.message; |
| } |
| |
| private Body getMessageBody(String language) { |
| language = determineLanguage(language); |
| for (Body body : bodies) { |
| if (language.equals(body.language)) { |
| return body; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a set of all bodies in this Message, including the default message body accessible |
| * from {@link #getBody()}. |
| * |
| * @return a collection of all bodies in this Message. |
| * @since 3.0.2 |
| */ |
| public Collection<Body> getBodies() { |
| return Collections.unmodifiableCollection(bodies); |
| } |
| |
| /** |
| * Sets the body of the message. The body is the main message contents. |
| * |
| * @param body the body of the message. |
| */ |
| public void setBody(String body) { |
| if (body == null) { |
| removeBody(""); // use empty string because #removeBody(null) is ambiguous |
| return; |
| } |
| addBody(null, body); |
| } |
| |
| /** |
| * Adds a body with a corresponding language. |
| * |
| * @param language the language of the body being added. |
| * @param body the body being added to the message. |
| * @return the new {@link org.jivesoftware.smack.packet.Message.Body} |
| * @throws NullPointerException if the body is null, a null pointer exception is thrown |
| * @since 3.0.2 |
| */ |
| public Body addBody(String language, String body) { |
| language = determineLanguage(language); |
| Body messageBody = new Body(language, body); |
| bodies.add(messageBody); |
| return messageBody; |
| } |
| |
| /** |
| * Removes the body with the given language from the message. |
| * |
| * @param language the language of the body which is to be removed |
| * @return true if a body was removed and false if it was not. |
| */ |
| public boolean removeBody(String language) { |
| language = determineLanguage(language); |
| for (Body body : bodies) { |
| if (language.equals(body.language)) { |
| return bodies.remove(body); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Removes the body from the message and returns true if the body was removed. |
| * |
| * @param body the body being removed from the message. |
| * @return true if the body was successfully removed and false if it was not. |
| * @since 3.0.2 |
| */ |
| public boolean removeBody(Body body) { |
| return bodies.remove(body); |
| } |
| |
| /** |
| * Returns all the languages being used for the bodies, not including the default body. |
| * |
| * @return the languages being used for the bodies. |
| * @since 3.0.2 |
| */ |
| public Collection<String> getBodyLanguages() { |
| Body defaultBody = getMessageBody(null); |
| List<String> languages = new ArrayList<String>(); |
| for (Body body : bodies) { |
| if (!body.equals(defaultBody)) { |
| languages.add(body.language); |
| } |
| } |
| return Collections.unmodifiableCollection(languages); |
| } |
| |
| /** |
| * Returns the thread id of the message, which is a unique identifier for a sequence |
| * of "chat" messages. If no thread id is set, <tt>null</tt> will be returned. |
| * |
| * @return the thread id of the message, or <tt>null</tt> if it doesn't exist. |
| */ |
| public String getThread() { |
| return thread; |
| } |
| |
| /** |
| * Sets the thread id of the message, which is a unique identifier for a sequence |
| * of "chat" messages. |
| * |
| * @param thread the thread id of the message. |
| */ |
| public void setThread(String thread) { |
| this.thread = thread; |
| } |
| |
| /** |
| * Returns the xml:lang of this Message. |
| * |
| * @return the xml:lang of this Message. |
| * @since 3.0.2 |
| */ |
| public String getLanguage() { |
| return language; |
| } |
| |
| /** |
| * Sets the xml:lang of this Message. |
| * |
| * @param language the xml:lang of this Message. |
| * @since 3.0.2 |
| */ |
| public void setLanguage(String language) { |
| this.language = language; |
| } |
| |
| private String determineLanguage(String language) { |
| |
| // empty string is passed by #setSubject() and #setBody() and is the same as null |
| language = "".equals(language) ? null : language; |
| |
| // if given language is null check if message language is set |
| if (language == null && this.language != null) { |
| return this.language; |
| } |
| else if (language == null) { |
| return getDefaultLanguage(); |
| } |
| else { |
| return language; |
| } |
| |
| } |
| |
| public String toXML() { |
| StringBuilder buf = new StringBuilder(); |
| buf.append("<message"); |
| if (getXmlns() != null) { |
| buf.append(" xmlns=\"").append(getXmlns()).append("\""); |
| } |
| if (language != null) { |
| buf.append(" xml:lang=\"").append(getLanguage()).append("\""); |
| } |
| if (getPacketID() != null) { |
| buf.append(" id=\"").append(getPacketID()).append("\""); |
| } |
| if (getTo() != null) { |
| buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\""); |
| } |
| if (getFrom() != null) { |
| buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\""); |
| } |
| if (type != Type.normal) { |
| buf.append(" type=\"").append(type).append("\""); |
| } |
| buf.append(">"); |
| // Add the subject in the default language |
| Subject defaultSubject = getMessageSubject(null); |
| if (defaultSubject != null) { |
| buf.append("<subject>").append(StringUtils.escapeForXML(defaultSubject.subject)).append("</subject>"); |
| } |
| // Add the subject in other languages |
| for (Subject subject : getSubjects()) { |
| // Skip the default language |
| if(subject.equals(defaultSubject)) |
| continue; |
| buf.append("<subject xml:lang=\"").append(subject.language).append("\">"); |
| buf.append(StringUtils.escapeForXML(subject.subject)); |
| buf.append("</subject>"); |
| } |
| // Add the body in the default language |
| Body defaultBody = getMessageBody(null); |
| if (defaultBody != null) { |
| buf.append("<body>").append(StringUtils.escapeForXML(defaultBody.message)).append("</body>"); |
| } |
| // Add the bodies in other languages |
| for (Body body : getBodies()) { |
| // Skip the default language |
| if(body.equals(defaultBody)) |
| continue; |
| buf.append("<body xml:lang=\"").append(body.getLanguage()).append("\">"); |
| buf.append(StringUtils.escapeForXML(body.getMessage())); |
| buf.append("</body>"); |
| } |
| if (thread != null) { |
| buf.append("<thread>").append(thread).append("</thread>"); |
| } |
| // Append the error subpacket if the message type is an error. |
| if (type == Type.error) { |
| XMPPError error = getError(); |
| if (error != null) { |
| buf.append(error.toXML()); |
| } |
| } |
| // Add packet extensions, if any are defined. |
| buf.append(getExtensionsXML()); |
| buf.append("</message>"); |
| return buf.toString(); |
| } |
| |
| |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| Message message = (Message) o; |
| |
| if(!super.equals(message)) { return false; } |
| if (bodies.size() != message.bodies.size() || !bodies.containsAll(message.bodies)) { |
| return false; |
| } |
| if (language != null ? !language.equals(message.language) : message.language != null) { |
| return false; |
| } |
| if (subjects.size() != message.subjects.size() || !subjects.containsAll(message.subjects)) { |
| return false; |
| } |
| if (thread != null ? !thread.equals(message.thread) : message.thread != null) { |
| return false; |
| } |
| return type == message.type; |
| |
| } |
| |
| public int hashCode() { |
| int result; |
| result = (type != null ? type.hashCode() : 0); |
| result = 31 * result + subjects.hashCode(); |
| result = 31 * result + (thread != null ? thread.hashCode() : 0); |
| result = 31 * result + (language != null ? language.hashCode() : 0); |
| result = 31 * result + bodies.hashCode(); |
| return result; |
| } |
| |
| /** |
| * Represents a message subject, its language and the content of the subject. |
| */ |
| public static class Subject { |
| |
| private String subject; |
| private String language; |
| |
| private Subject(String language, String subject) { |
| if (language == null) { |
| throw new NullPointerException("Language cannot be null."); |
| } |
| if (subject == null) { |
| throw new NullPointerException("Subject cannot be null."); |
| } |
| this.language = language; |
| this.subject = subject; |
| } |
| |
| /** |
| * Returns the language of this message subject. |
| * |
| * @return the language of this message subject. |
| */ |
| public String getLanguage() { |
| return language; |
| } |
| |
| /** |
| * Returns the subject content. |
| * |
| * @return the content of the subject. |
| */ |
| public String getSubject() { |
| return subject; |
| } |
| |
| |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + this.language.hashCode(); |
| result = prime * result + this.subject.hashCode(); |
| return result; |
| } |
| |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| Subject other = (Subject) obj; |
| // simplified comparison because language and subject are always set |
| return this.language.equals(other.language) && this.subject.equals(other.subject); |
| } |
| |
| } |
| |
| /** |
| * Represents a message body, its language and the content of the message. |
| */ |
| public static class Body { |
| |
| private String message; |
| private String language; |
| |
| private Body(String language, String message) { |
| if (language == null) { |
| throw new NullPointerException("Language cannot be null."); |
| } |
| if (message == null) { |
| throw new NullPointerException("Message cannot be null."); |
| } |
| this.language = language; |
| this.message = message; |
| } |
| |
| /** |
| * Returns the language of this message body. |
| * |
| * @return the language of this message body. |
| */ |
| public String getLanguage() { |
| return language; |
| } |
| |
| /** |
| * Returns the message content. |
| * |
| * @return the content of the message. |
| */ |
| public String getMessage() { |
| return message; |
| } |
| |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + this.language.hashCode(); |
| result = prime * result + this.message.hashCode(); |
| return result; |
| } |
| |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| Body other = (Body) obj; |
| // simplified comparison because language and message are always set |
| return this.language.equals(other.language) && this.message.equals(other.message); |
| } |
| |
| } |
| |
| /** |
| * Represents the type of a message. |
| */ |
| public enum Type { |
| |
| /** |
| * (Default) a normal text message used in email like interface. |
| */ |
| normal, |
| |
| /** |
| * Typically short text message used in line-by-line chat interfaces. |
| */ |
| chat, |
| |
| /** |
| * Chat message sent to a groupchat server for group chats. |
| */ |
| groupchat, |
| |
| /** |
| * Text message to be displayed in scrolling marquee displays. |
| */ |
| headline, |
| |
| /** |
| * indicates a messaging error. |
| */ |
| error; |
| |
| public static Type fromString(String name) { |
| try { |
| return Type.valueOf(name); |
| } |
| catch (Exception e) { |
| return normal; |
| } |
| } |
| |
| } |
| } |