001    package com.skype.api;
002    
003    import java.io.IOException;
004    import java.util.*;
005    import com.skype.ipc.*;
006    /**
007     * Events in a conversation context are expressed as Messages. It is therefore useful to think of Message objects as events, rather than specifically text chat messages. <br><br>Message member functions all return a Boolean indicating the success (true) or failure (false) of processing the request itself (transport, runtime availability, and so forth) - not the success or failure of its associated functionality. For example, Message.Edit returns true if it was able to make a determination, and its result parameter reflects whether this Message can be edited. Similarly, Message.Edit returns false if it was unable to make a determination, and the value of its result parameter is undefined. <br><br>Message member functions that are specific to a Message TYPE return false if this Message is not of that type. For example, Message.GetVoiceMessage will return false if this Message's type is not POSTED_VOICE_MESSAGE. <br><br>The actual meaning of a Message can be determined by its P_TYPE property. The meanings of most other Message properties depend on the value of P_TYPE. For example, let's take P_BODY_XML property. <br><br>Following messages have a text entered by the user as a body. It may contain emoticons, URLs, etc. <br> - POSTED_TEXT <br> - POSTED_EMOTE <br> - SET_METADATA <br> - REQUESTED_AUTH <br><br>Following messages have a custom XML format for the body (see the specific section on these message types for details): <br> - POSTED_CONTACTS <br> - POSTED_VOICE_MESSAGE <br> - POSTED_FILES <br> - POSTED_SMS <br> - STARTED_LIVESESSION and ENDED_LIVESESSION (same format) <br><br>Following messages do not use the body property: <br> - SPAWNED_CONFERENCE <br> - ADDED_CONSUMERS <br> - ADDED_APPLICANTS <br> - RETIRED_OTHERS <br> - RETIRED <br> - SET_RANK <br> - HAS_BIRTHDAY <br> - GRANTED_AUTH <br> - BLOCKED <br><br>Messages such as POSTED_TEXT use a small subset of a HTML-like markup to control the visual representation of the text. This markup is used by POSTED_TEXT and POSTED_EMOTE, but also for the conversation topic (CONVERSATION_META_TOPIC property and the body of the SET_METADATA message) and for authorization requests. <br><br>Having chat messages in XML format means that all formatting is indicated by XML tags. This includes emoticons and URls. The big advantage is that it makes the parsing of the message by the UI much easier. The UI does not need to do emoticons or URL detection, this is already done and it only needs to look for the XML tags. <br><br>For text messages, it is possible for the UI to simply ignore (meaning strip) the XML and the message will be understandable fine, it will only have lost some formatting. <br><br>But it is obviously nicer to display at least the most commonly used tags. <br><br>To strip the XML: <br> - if they have the alt="sometext" attribute set, return sometext as the output of that tag and ignore the rest of tag and all nested sub tags <br> - if no alt="" attribute set, use tag content as output - <sometag>hereissomething</sometag> is output as hereissomething<br> - if no alt="" and no tag content, ignore the tag altogether (return nothing) <br>Skype for Windows supports displaying many XML tags, but only a sub-set is regularly used and should be supported by the UI for a good experience. These are the ones described here. <br>Animated emoticons <br>Emoticons are encoded with the "ss" tag. The element content is the plain text representation. It has a "type" attribute indicating the emoticons canonical name. Example:  <br>@code <br>Hi <ss type="smile">:-)</ss>  <br></CODE> <br><br>Flag emoticons <br>Flag emoticons are little flags. They are encoded with the "flag" tag. The element contents is the plain text representation and it has a "country" attribute which is a 2-letter ISO-3166 country code. The user can enter a flag using "(flag:XX)", where XX is a valid ISO country code. Example:  <br>@code <br>I am in <flag country="cc">CC</flag>  <br></CODE> <br><br>Links <br>If the library detects a URL, it will encode it using the html "a" tag, with the "href" attribute indicating the URL. The plain text representation is what the user originally typed. Example:  <br>@code <br>I am in <a href="http://wwww.skype.com">www.skype.com</a> <br></CODE> <br><br>Alert matches <br>When a conversation is configured to display only alerts if specific words are present in the message (see "/alertson [text to match]" command), if a message matches the alert, it will be marked with the <alertmatch> tag. This allows the UI to highlight the word matching. Example:  <br>@code <br>Maybe <alertmatch>Vincent</alertmatch> knows the answer  <br></CODE> <br><br>Bold, italic, etc <br>Skype for Windows also supports displaying bold and italic text, using the "b" and "i" tags. <br><br>Encoding messages <br>When sending a chat message via PostText(), there is the possibility to indicate if the library should do the XML encoding, or if the message has already been encoded. Usually, the UI can let library do the encoding. This is the case when the message does not contain any specific formatting. It may contain emoticons or URls, which will be detected by the library encoder and converted into XML tags. <br>If the message has some more complex encoding, such as a quote or some bold text, it is up to the UI to encode the message. <br>
008     */
009    
010    
011    public class Message extends SkypeObject {
012    
013    
014            public interface MessageListener {
015                    /** This event gets called when there are changes to Message properties defined in Message.PROPERTY  */
016                    public void OnPropertyChange(SkypeObject obj, PROPERTY prop, Object value);
017                    
018            }
019            
020            public Message(int oid, Skype skype) {
021                    super(oid,skype);
022                    /**get default properties for this module **/
023                    GetDefaultProps();
024            }
025            
026            private static final int MODULE_ID = 9;
027            
028            public static final int moduleID() {
029                    return MODULE_ID;
030            }
031            
032            /** Properties of the Message class */
033            public enum PROPERTY {
034            
035                    /** DB ID of corresponding conversation, type: Conversation */
036                    convo_id(960),
037                    
038                    /** GUID of the Conversation. The GUID is a "global ID" - these values are shared accross Skype client instances and accross all the participants of the conversation. <br>, type: String */
039                    convo_guid(120),
040                    
041                    /** Identity of the sender. While this is almost always the same as SKYPENAME property of the Contact, in some rare cases it can also be a phone number - for example, incoming voicemail notification Messages (message type = POSTED_VOICE_MESSAGE). <br>, type: String */
042                    author(122),
043                    
044                    /** displayname of the sender at the time of posting, type: String */
045                    author_displayname(123),
046                    
047                    /** Unlike the message id, the GUID is the same on all instances and for all participants. <br>, type: byte[] */
048                    guid(792),
049                    
050                    /** This property gets set when a conference is spawned from dialog Conversation. In that case recent message history from the original dialog is copied to the target conversation. For all the copied messages, the ORIGINALLY_MEANT_FOR property will be set to identity of the remote participant of the original dialog. <br>, type: String */
051                    originally_meant_for(790),
052                    
053                    /** UNIX timestamp (sent time, adjusted for local clock), type: int */
054                    timestamp(121),
055                    
056                    /** type: Message.TYPE */
057                    type(961),
058                    
059                    /** type: Message.SENDING_STATUS */
060                    sending_status(962),
061                    
062                    /** type: Message.CONSUMPTION_STATUS */
063                    consumption_status(968),
064                    
065                    /** Identity of the author that last edited this message. NULL if message has not been edited <br>, type: String */
066                    edited_by(222),
067                    
068                    /** UNIX timestamp of last edit, type: int */
069                    edit_timestamp(223),
070                    
071                    /** Message type-specific parameter. See Message.SET_METADATA_KEY for more information. <br>, type: int */
072                    param_key(963),
073                    
074                    /** Message type-specific parameter <br>, type: int */
075                    param_value(964),
076                    
077                    /** Message type-specific parameter <br>, type: String */
078                    body_xml(127),
079                    
080                    /** Message type-specific parameter. Depending of Message type, this property contains: <br> - STARTED_LIVESESSION - list of participants in the cal; <br> - ENDED_LIVESESSION - list of participants in the call; <br> - POSTED_SMS - list of recipients of the message; <br> - SPAWNED_CONFERENCE - the list of identities that were added; <br> - ADDED_CONSUMERS - the list of identities that were added; <br> - RETIRED_OTHERS - the skypename of the participant who was kicked; <br> - SET_RANK - the skypename of the participant whose rank was changed; <br> - REQUESTED_AUTH - Message.P_AUTHOR and Message.P_IDENTITIES are set to the users receiving and requesting the authorization, depending if the message was received or sent; <br> - GRANTED_AUTH - the skypename of the user we granted authorization; <br> - BLOCKED - the skypename of the user who was blocked; <br> - HAS_BIRTHDAY - skypename of current logged in user. <br>, type: String */
081                    identities(125),
082                    
083                    /** Message type-specific parameter. Possible values for STARTED/ENDED_LIVESESSION (only set for dialogs): <br> - no_answer  <br> - manual  <br> - busy  <br> - connection_dropped <br> - no_skypeout_subscription; <br> - insufficient_funds <br> - internet_connection_lost <br> - skypeout_account_blocked <br> - pstn_could_not_connect_to_skype_proxy <br> - pstn_invalid_number <br> - pstn_number_forbidden <br> - pstn_call_timed_out <br> - pstn_busy <br> - pstn_call_terminated <br> - pstn_network_error <br> - number_unavailable <br> - pstn_call_rejected <br> - pstn_misc_error <br> - internal_error <br> - unable_to_connect <br> - connection_dropped <br> - recording_failed <br> - playback_error <br> - legacy_error <br> - blocked_by_privacy_settings <br> - error <br> - transfer_failed <br> - transfer_insufficient_funds <br> - blocked_by_us <br> - emergency_call_denied <br><br>This information is now available as an enum in LEAVEREASON <br>, type: String */
084                    reason(966),
085                    
086                    /** Leave reason for message of the RETIRED type, and STARTED/ENDED_LIVESESSION.                   Use for STARTED/ENDED_LIVESESSION is to provide simpler, enum based                   handling and deprecates the reason property (only set for dialogs), type: LEAVE_REASON */
087                    leavereason(126),
088                    
089                    /** Number of people who received this message (including local user) <br>, type: int */
090                    participant_count(982);
091                    
092                    private static final Map<Integer,PROPERTY> lookup = new HashMap<Integer,PROPERTY>();
093                    
094                    static {
095                            for(PROPERTY s : EnumSet.allOf(PROPERTY.class))
096                                    lookup.put(s.getId(), s);
097                    }
098                    
099                    private final int id;
100                    
101                    private PROPERTY(int value) {
102                            this.id = value;
103                    }
104                    
105                    public int getId() { return id; }
106                    
107                    public static PROPERTY get(int code) {
108                            return lookup.get(code);
109                    }
110                    
111                    public static PROPERTY fromString(String s) {
112                            for (PROPERTY p : lookup.values()) {
113                                    if (p.toString() == s) {
114                                            return p;
115                                    }
116                            }
117                            return null;
118                    }
119            }
120            
121            public Object GetPropertyAsEnum(int propid) {
122                    return PROPERTY.get(propid);
123            }
124            
125            public String GetStrProperty(PROPERTY prop) {
126                    //check in propcache if so then return
127                    if (mPropCache.containsKey(new Integer(prop.id))){
128                            String value =  (String)mPropCache.get(prop.id);
129                            if (value != null && !(value.length() == 0) ){
130                                    return value;
131                            }
132                    }
133                    //else get from skypekit...
134                    GetPropertyRequest request = new GetPropertyRequest(9, mObjectId, prop.id);
135                    
136                    String string = null;
137                    GetPropertyResponse r = skype.GetProperty(request);
138                    if (r != null){
139                            string = r.GetAsString();
140                    }
141                    
142                    if (string != null)
143                    {
144                            mPropCache.put(new Integer(prop.id), string);
145                    }
146                    return string;
147            }
148            
149            public int GetIntProperty(PROPERTY prop) {
150                    //check in propcache if so then return
151                    if (mPropCache.containsKey(new Integer(prop.id))){
152                            int value = ((Integer)mPropCache.get(prop.id)).intValue();
153                            if (value != 0){
154                                    return value;
155                            }
156                    }
157                    //else get from skypekit...
158                    GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
159                    
160                    Integer integer = null;
161                    GetPropertyResponse r = skype.GetProperty(request);
162                    if (r != null){
163                            integer  = r.GetAsInt();
164                    }
165                    
166                    if (integer != null)
167                    {
168                            mPropCache.put(new Integer(prop.id), integer);
169                            return integer.intValue();
170                    }
171                    else
172                    {
173                            return 0;
174                    }
175            }
176            
177            public boolean GetBooleanProperty(PROPERTY prop) {
178                    //check in propcache if so then return
179                    if (mPropCache.containsKey(new Integer(prop.id))){
180                            return ((Boolean)mPropCache.get(prop.id)).booleanValue();
181                    }
182                    //else get from skypekit...
183                    GetPropertyRequest request = new GetPropertyRequest(moduleID(), mObjectId, prop.id);
184                    
185                    Boolean boolResp = null;
186                    GetPropertyResponse r = skype.GetProperty(request);
187                    if (r != null){
188                            boolResp  = r.GetAsBoolean();
189                    }
190                    
191                    if (boolResp != null)
192                    {
193                            mPropCache.put(new Integer(prop.id), boolResp);
194                            return boolResp.booleanValue();
195                    }
196                    else
197                    {
198                            return false;
199                    }
200            }
201            
202            public byte [] GetBinProperty(PROPERTY prop) {
203                    //get from skypekit...
204                    GetPropertyRequest request = new GetPropertyRequest(9, mObjectId, prop.id);
205                    
206                    byte [] data = null;
207                    GetPropertyResponse r = skype.GetProperty(request);
208                    if (r != null) {
209                            data = r.GetAsBinary();
210                    }
211                    return data;
212            }
213            
214            /**default array of Message Properties that get fetched & cached upon class construction*/
215            private static PROPERTY [] defaultProperties = { PROPERTY.convo_id, PROPERTY.type, PROPERTY.author, PROPERTY.author_displayname, PROPERTY.identities, PROPERTY.timestamp, PROPERTY.body_xml, PROPERTY.sending_status, PROPERTY.consumption_status};
216            
217            private void GetDefaultProps() {
218                    MultiGetPropertyRequest request = null;
219                    ArrayList<Integer> proparray = null;
220                            /**Add the single oid into array*/
221                            ArrayList<Integer> oidarray=new ArrayList<Integer>();
222                            oidarray.add(mObjectId);
223                            
224                            /**Add all requested propids into array*/
225                            proparray=new ArrayList<Integer>();
226                            for (PROPERTY defaultProp : defaultProperties) {
227                                    proparray.add(defaultProp.getId());
228                            }
229                            /**Generate the request*/
230                            request = new MultiGetPropertyRequest(moduleID(), oidarray,proparray);
231                            
232                            /** Make Multi Get call*/
233                            GetPropertyResponse r=skype.MultiGetProperty(request);
234                            /**Verify that it is a proper multiresponse*/
235                            if(!r.isMultiresponse())
236                            {
237                                    return;
238                            }
239                            /**update property cache with results*/
240                            mPropCache.putAll(r.GetAsMap(mObjectId, proparray));
241                    }
242                    
243                    /**
244                    The P_TYPE property determines the actual meaning of the Message object. Only Messages of POSTED_TEXT type contain actual text messages. The meaning and content of the rest of the message properties are largely dependant of the value of the Message.P_TYPE. <br> */
245                    public enum TYPE {
246                    
247                            /** Conference metadata were changed*/
248                            SET_METADATA(2),
249                            
250                            /** A conference was spawned from this dialog*/
251                            SPAWNED_CONFERENCE(4),
252                            
253                            /** Some users were added to the conference*/
254                            ADDED_CONSUMERS(10),
255                            
256                            /** Some users are applying to be added to the conference*/
257                            ADDED_APPLICANTS(11),
258                            
259                            /** User was kicked from the conference*/
260                            RETIRED_OTHERS(12),
261                            
262                            /** User left the conference*/
263                            RETIRED(13),
264                            
265                            /** Changed the rank of a user in the Conversation (multichat administration) <br>*/
266                            SET_RANK(21),
267                            
268                            /** A live session started*/
269                            STARTED_LIVESESSION(30),
270                            
271                            /** A live session ended*/
272                            ENDED_LIVESESSION(39),
273                            
274                            /** User requested authorization*/
275                            REQUESTED_AUTH(50),
276                            
277                            /** User was granted authorization. Notification message that user is now an authorized contact (of the local user). <br>*/
278                            GRANTED_AUTH(51),
279                            
280                            /** User was blocked*/
281                            BLOCKED(53),
282                            
283                            /** A text message*/
284                            POSTED_TEXT(61),
285                            
286                            /** An emote ('John Doe is laughing', cf /me chat command)*/
287                            POSTED_EMOTE(60),
288                            
289                            /** The message represents (a set of) contact card(s) posted in the conversation. One message can contain more than one contact cards. The contacts can be retrieved from the message by parsing them out from the P_BODY_XML property. For more information, see Conversation.PostContacts <br>*/
290                            POSTED_CONTACTS(63),
291                            
292                            /** The message represents an SMS object that was posted in the Conversation. See Conversation.PostSMS for more details. The Sms object itself can be retrieved from the Message with Message.GetSms The message BODY_XML contains a set of SMS properties, such as status, failurereason, targets, price and timestamp. <br>*/
293                            POSTED_SMS(64),
294                            
295                            /** Deprecated, never sent*/
296                            POSTED_ALERT(65),
297                            
298                            /** A voicemail*/
299                            POSTED_VOICE_MESSAGE(67),
300                            
301                            /** The message represents a (list of) file transfers that were posted in the Conversation with Conversation.PostFiles. Transfer objects can be retrieved from the Message with Message.GetTransfers <br>*/
302                            POSTED_FILES(68),
303                            
304                            /** Currently unused. <br>*/
305                            POSTED_INVOICE(69),
306                            
307                            /** The message represents a Contact birthday notification. <br>*/
308                            HAS_BIRTHDAY(110);
309                            
310                            private static final Map<Integer,TYPE> lookup = new HashMap<Integer,TYPE>();
311                            
312                            static {
313                                    for(TYPE s : EnumSet.allOf(TYPE.class))
314                                            lookup.put(s.getId(), s);
315                            }
316                            
317                            private final int id;
318                            
319                            private TYPE(int value) {
320                                    this.id = value;
321                            }
322                            
323                            public int getId() { return id; }
324                            
325                            public static TYPE get(int code) {
326                                    return lookup.get(code);
327                            }
328                            
329                            public static TYPE fromString(String s) {
330                                    for (TYPE p : lookup.values()) {
331                                            if (p.toString() == s) {
332                                                    return p;
333                                            }
334                                    }
335                                    return null;
336                            }
337                    }
338                    
339                    /**
340                     */
341                    public enum SENDING_STATUS {
342                    
343                            /** Message has not been delivered to at least one of the participants <br>*/
344                            SENDING(1),
345                            
346                            /** Message has been delivered to at least one other participant <br>*/
347                            SENT(2),
348                            
349                            /** Message could not be delivered (for SMS this reflects the actual SMS, not the chat message) <br>*/
350                            FAILED_TO_SEND(3);
351                            
352                            private static final Map<Integer,SENDING_STATUS> lookup = new HashMap<Integer,SENDING_STATUS>();
353                            
354                            static {
355                                    for(SENDING_STATUS s : EnumSet.allOf(SENDING_STATUS.class))
356                                            lookup.put(s.getId(), s);
357                            }
358                            
359                            private final int id;
360                            
361                            private SENDING_STATUS(int value) {
362                                    this.id = value;
363                            }
364                            
365                            public int getId() { return id; }
366                            
367                            public static SENDING_STATUS get(int code) {
368                                    return lookup.get(code);
369                            }
370                            
371                            public static SENDING_STATUS fromString(String s) {
372                                    for (SENDING_STATUS p : lookup.values()) {
373                                            if (p.toString() == s) {
374                                                    return p;
375                                            }
376                                    }
377                                    return null;
378                            }
379                    }
380                    
381                    /**
382                    Indicates if a message has been consumed (meaning read) or not */
383                    public enum CONSUMPTION_STATUS {
384                    
385                            /** Message has been read. Note that this is a read-only property. Consumption status of individual messages can not be set selectively. Message consumption status is determined at the conversation level, based conversation consumption horizon and individual message timestamps. Conversation consumption horizon can be updated with Conversation.SetConsumedHorizon method.  <br>*/
386                            CONSUMED(0),
387                            
388                            /** Do not notify the user that they have this unread message <br>*/
389                            UNCONSUMED_SUPPRESSED(1),
390                            
391                            /** Notify the user that they have this unread message <br>*/
392                            UNCONSUMED_NORMAL(2),
393                            
394                            /** This message consumption state is marked as DEPRECATED <br>*/
395                            UNCONSUMED_ELEVATED(3);
396                            
397                            private static final Map<Integer,CONSUMPTION_STATUS> lookup = new HashMap<Integer,CONSUMPTION_STATUS>();
398                            
399                            static {
400                                    for(CONSUMPTION_STATUS s : EnumSet.allOf(CONSUMPTION_STATUS.class))
401                                            lookup.put(s.getId(), s);
402                            }
403                            
404                            private final int id;
405                            
406                            private CONSUMPTION_STATUS(int value) {
407                                    this.id = value;
408                            }
409                            
410                            public int getId() { return id; }
411                            
412                            public static CONSUMPTION_STATUS get(int code) {
413                                    return lookup.get(code);
414                            }
415                            
416                            public static CONSUMPTION_STATUS fromString(String s) {
417                                    for (CONSUMPTION_STATUS p : lookup.values()) {
418                                            if (p.toString() == s) {
419                                                    return p;
420                                            }
421                                    }
422                                    return null;
423                            }
424                    }
425                    
426                    /**
427                    For messages of type SET_METADATA that alert participants to changes to the associated Conversation's metadata, indicates which metadata property changed and its P_BODY_XML property contains the changed data. Your UI is expected to detect messages with PARAM_KEY set and to update its visual representation of Conversation accordingly. <br>You can use the associated Conversation's properties and methods to obtain the updated metadata rather than parse the message body XML, for example, Conversation.P_META_PICTURE and Conversation.Conversation.GetPropMetaPicture. <br> */
428                    public enum SET_METADATA_KEY {
429                    
430                            /** Notification message that conversation name has changed. <br>*/
431                            SET_META_NAME(3640),
432                            
433                            /** Notification message that conversation topic has changed. <br>*/
434                            SET_META_TOPIC(3644),
435                            
436                            /** Notification message that conversation guidelines have changed. <br>*/
437                            SET_META_GUIDELINES(3652),
438                            
439                            /** Notification message that conversation picture has changed. <br>*/
440                            SET_META_PICTURE(3658);
441                            
442                            private static final Map<Integer,SET_METADATA_KEY> lookup = new HashMap<Integer,SET_METADATA_KEY>();
443                            
444                            static {
445                                    for(SET_METADATA_KEY s : EnumSet.allOf(SET_METADATA_KEY.class))
446                                            lookup.put(s.getId(), s);
447                            }
448                            
449                            private final int id;
450                            
451                            private SET_METADATA_KEY(int value) {
452                                    this.id = value;
453                            }
454                            
455                            public int getId() { return id; }
456                            
457                            public static SET_METADATA_KEY get(int code) {
458                                    return lookup.get(code);
459                            }
460                            
461                            public static SET_METADATA_KEY fromString(String s) {
462                                    for (SET_METADATA_KEY p : lookup.values()) {
463                                            if (p.toString() == s) {
464                                                    return p;
465                                            }
466                                    }
467                                    return null;
468                            }
469                    }
470                    
471                    /**
472                    Indicates the reason a user could not join or left a Conversation. SkypeKit automatically sets "could not join"-related values. "Left voluntarily"-related values are set as a result of explicit user actions. <br> */
473                    public enum LEAVEREASON {
474                    
475                            /** User cannot chat (user is currently logged in with a client that has chat disabled - see Contact.CAPABILITY.CAPABILITY_TEXT) <br>*/
476                            USER_INCAPABLE(2),
477                            
478                            /** Attempt to add local user to a conversation by an unknown contact <br>*/
479                            ADDER_MUST_BE_FRIEND(3),
480                            
481                            /** Attempt to add local user to a conversation by an unauthorized contact <br>*/
482                            ADDER_MUST_BE_AUTHORIZED(4),
483                            
484                            /** Local user declined an "invitation" to join a chat <br>*/
485                            DECLINE_ADD(5),
486                            
487                            /** User decided to end participation in an on-going multi-chat <br>*/
488                            UNSUBSCRIBE(6);
489                            
490                            private static final Map<Integer,LEAVEREASON> lookup = new HashMap<Integer,LEAVEREASON>();
491                            
492                            static {
493                                    for(LEAVEREASON s : EnumSet.allOf(LEAVEREASON.class))
494                                            lookup.put(s.getId(), s);
495                            }
496                            
497                            private final int id;
498                            
499                            private LEAVEREASON(int value) {
500                                    this.id = value;
501                            }
502                            
503                            public int getId() { return id; }
504                            
505                            public static LEAVEREASON get(int code) {
506                                    return lookup.get(code);
507                            }
508                            
509                            public static LEAVEREASON fromString(String s) {
510                                    for (LEAVEREASON p : lookup.values()) {
511                                            if (p.toString() == s) {
512                                                    return p;
513                                            }
514                                    }
515                                    return null;
516                            }
517                    }
518                    
519                    /**
520                     *For Message types having a body, determines whether that body is editable by the user.  <br>
521                     * @return result
522                     */
523                    public boolean CanEdit() {
524                    
525                            Request request = null;
526                            try {
527                                    request = new XCallRequest(9,1);
528                            } catch (IOException e) {
529                                    e.printStackTrace();
530                                    if (skype.errorListener != null)
531                                            skype.errorListener.OnSkypeKitFatalError();
532                            }
533                            request.addParm('O',0,mObjectId);
534                            
535                            Response r = skype.XCall((XCallRequest)request);
536                            
537                            if (r.isErrCall())
538                                    return false;
539                                    
540                            boolean result = false;
541                            result = r.GetAsBoolean(1);
542                            return result;
543                    }
544                    
545                    /**
546                     *For Message types that include a body and are editable:  <br> - alters BODY_XML of the message object <br> - sets EDITED_BY and EDIT_TIMESTAMP properties  <br> - propagates the changes to remote users. <br>
547                     * @param newText New value of the message BODY_XML property. <br>
548                     * @param isXML Specify isXML as true if the message body is formatted as XML; omit it or specify it as false if the message body is plain text. <br>
549                     * @param undo Reverts the message body to the original version. newText parameter is ignored when this is set
550                     */
551                    public void Edit( String newText, boolean isXML, boolean undo) {
552                    
553                            Request request = null;
554                            try {
555                                    request = new XCallRequest(9,2);
556                            } catch (IOException e) {
557                                    e.printStackTrace();
558                                    if (skype.errorListener != null)
559                                            skype.errorListener.OnSkypeKitFatalError();
560                            }
561                            request.addParm('O',0,mObjectId);
562                            request.addParm('S',1,newText);
563                            request.addParm('b',2,isXML);
564                            request.addParm('b',3,undo);
565                            
566                            skype.XCall((XCallRequest)request);
567                    }
568                    
569                    /**
570                     *For messages of type POSTED_CONTACTS, parses the body XML and formats the data as a list of Contact instances. <br>
571                     * @return contacts
572                     */
573                    public Contact [] GetContacts() {
574                    
575                            Request request = null;
576                            try {
577                                    request = new XCallRequest(9,3);
578                            } catch (IOException e) {
579                                    e.printStackTrace();
580                                    if (skype.errorListener != null)
581                                            skype.errorListener.OnSkypeKitFatalError();
582                            }
583                            request.addParm('O',0,mObjectId);
584                            
585                            Response r = skype.XCall((XCallRequest)request);
586                            
587                            if (r.isErrCall())
588                                    return null;
589                                    
590                            Vector<Contact> contacts = new Vector<Contact>();
591                            while (r.HasMore(1))
592                            {
593                                    int oid = 0;
594                                    Contact contact = null;
595                                    oid = r.GetOid(1);
596                                    if (oid != AbstractDecoder.NULL_VALUE) { 
597                                            contact = (Contact)skype.factory(Contact.moduleID(), oid, skype);
598                                    }
599                                    contacts.add(contact);
600                            }
601                            return contacts.toArray(new Contact[contacts.size()]);
602                            
603                    }
604                    
605                    /**
606                     *For messages of type POSTED_FILES, parses the body XML and creates a list of Transfer instances. <br>
607                     * @return transfers
608                     */
609                    public Transfer [] GetTransfers() {
610                    
611                            Request request = null;
612                            try {
613                                    request = new XCallRequest(9,4);
614                            } catch (IOException e) {
615                                    e.printStackTrace();
616                                    if (skype.errorListener != null)
617                                            skype.errorListener.OnSkypeKitFatalError();
618                            }
619                            request.addParm('O',0,mObjectId);
620                            
621                            Response r = skype.XCall((XCallRequest)request);
622                            
623                            if (r.isErrCall())
624                                    return null;
625                                    
626                            Vector<Transfer> transfers = new Vector<Transfer>();
627                            while (r.HasMore(1))
628                            {
629                                    int oid = 0;
630                                    Transfer transfer = null;
631                                    oid = r.GetOid(1);
632                                    if (oid != AbstractDecoder.NULL_VALUE) { 
633                                            transfer = (Transfer)skype.factory(Transfer.moduleID(), oid, skype);
634                                    }
635                                    transfers.add(transfer);
636                            }
637                            return transfers.toArray(new Transfer[transfers.size()]);
638                            
639                    }
640                    
641                    /**
642                     *For messages of type POSTED_VOICE_MESSAGE, parses the body XML and creates a Voicemail instance. <br>
643                     * @return voicemail
644                     */
645                    public Voicemail GetVoiceMessage() {
646                    
647                            Request request = null;
648                            try {
649                                    request = new XCallRequest(9,5);
650                            } catch (IOException e) {
651                                    e.printStackTrace();
652                                    if (skype.errorListener != null)
653                                            skype.errorListener.OnSkypeKitFatalError();
654                            }
655                            request.addParm('O',0,mObjectId);
656                            
657                            Response r = skype.XCall((XCallRequest)request);
658                            
659                            if (r.isErrCall())
660                                    return null;
661                                    
662                            int oid = 0;
663                            Voicemail voicemail = null;
664                            oid = r.GetOid(1);
665                            if (oid != AbstractDecoder.NULL_VALUE) {
666                                    voicemail = (Voicemail)skype.factory(Voicemail.moduleID(), oid, skype);
667                            }
668                            return voicemail;
669                    }
670                    
671                    /**
672                     *For messages of type POSTED_SMS, parses the body XML and creates an SMS instances <br>
673                     * @return sms
674                     */
675                    public Sms GetSMS() {
676                    
677                            Request request = null;
678                            try {
679                                    request = new XCallRequest(9,6);
680                            } catch (IOException e) {
681                                    e.printStackTrace();
682                                    if (skype.errorListener != null)
683                                            skype.errorListener.OnSkypeKitFatalError();
684                            }
685                            request.addParm('O',0,mObjectId);
686                            
687                            Response r = skype.XCall((XCallRequest)request);
688                            
689                            if (r.isErrCall())
690                                    return null;
691                                    
692                            int oid = 0;
693                            Sms sms = null;
694                            oid = r.GetOid(1);
695                            if (oid != AbstractDecoder.NULL_VALUE) {
696                                    sms = (Sms)skype.factory(Sms.moduleID(), oid, skype);
697                            }
698                            return sms;
699                    }
700                    
701                    /**
702                     *Deletes this message from the local database. These deletions do not propagate to the other Skype instances that the user may have on other computers. Nor do they affect other participants that have the same message. This method is specifically from removing Message objects from the database - not for removing Messages from conversations. To remove a Message from a conversation, use Message.Edit method to replace the existing body text with an empty string. <br>
703                     */
704                    public void DeleteLocally() {
705                    
706                            Request request = null;
707                            try {
708                                    request = new XCallRequest(9,8);
709                            } catch (IOException e) {
710                                    e.printStackTrace();
711                                    if (skype.errorListener != null)
712                                            skype.errorListener.OnSkypeKitFatalError();
713                            }
714                            request.addParm('O',0,mObjectId);
715                            
716                            skype.XCall((XCallRequest)request);
717                    }
718                    
719            
720            }