    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, type: Message.LEAVEREASON */
    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                     */
    550                    public void Edit( String newText, boolean isXML) {
    551
    552                            Request request = null;
    553                            try {
    554                                    request = new XCallRequest(9,2);
    555                            } catch (IOException e) {
    556                                    e.printStackTrace();
    557                                    if (skype.errorListener != null)
    558                                            skype.errorListener.OnSkypeKitFatalError();
    559                            }
    560                            request.addParm('O',0,mObjectId);
    561                            request.addParm('S',1,newText);
    562                            request.addParm('b',2,isXML);
    563
    564                            skype.XCall((XCallRequest)request);
    565                    }
    566
    567                    /**
    568                     *For messages of type POSTED_CONTACTS, parses the body XML and formats the data as a list of Contact instances. <br>
    569                     * @return contacts
    570                     */
    571                    public Contact [] GetContacts() {
    572
    573                            Request request = null;
    574                            try {
    575                                    request = new XCallRequest(9,3);
    576                            } catch (IOException e) {
    577                                    e.printStackTrace();
    578                                    if (skype.errorListener != null)
    579                                            skype.errorListener.OnSkypeKitFatalError();
    580                            }
    581                            request.addParm('O',0,mObjectId);
    582
    583                            Response r = skype.XCall((XCallRequest)request);
    584
    585                            if (r.isErrCall())
    586                                    return null;
    587
    588                            Vector<Contact> contacts = new Vector<Contact>();
    589                            while (r.HasMore(1))
    590                            {
    591                                    int oid = 0;
    592                                    Contact contact = null;
    593                                    oid = r.GetOid(1);
    594                                    if (oid != AbstractDecoder.NULL_VALUE) {
    595                                            contact = (Contact)skype.factory(Contact.moduleID(), oid, skype);
    596                                    }
    597                                    contacts.add(contact);
    598                            }
    599                            return contacts.toArray(new Contact[contacts.size()]);
    600
    601                    }
    602
    603                    /**
    604                     *For messages of type POSTED_FILES, parses the body XML and creates a list of Transfer instances. <br>
    605                     * @return transfers
    606                     */
    607                    public Transfer [] GetTransfers() {
    608
    609                            Request request = null;
    610                            try {
    611                                    request = new XCallRequest(9,4);
    612                            } catch (IOException e) {
    613                                    e.printStackTrace();
    614                                    if (skype.errorListener != null)
    615                                            skype.errorListener.OnSkypeKitFatalError();
    616                            }
    617                            request.addParm('O',0,mObjectId);
    618
    619                            Response r = skype.XCall((XCallRequest)request);
    620
    621                            if (r.isErrCall())
    622                                    return null;
    623
    624                            Vector<Transfer> transfers = new Vector<Transfer>();
    625                            while (r.HasMore(1))
    626                            {
    627                                    int oid = 0;
    628                                    Transfer transfer = null;
    629                                    oid = r.GetOid(1);
    630                                    if (oid != AbstractDecoder.NULL_VALUE) {
    631                                            transfer = (Transfer)skype.factory(Transfer.moduleID(), oid, skype);
    632                                    }
    633                                    transfers.add(transfer);
    634                            }
    635                            return transfers.toArray(new Transfer[transfers.size()]);
    636
    637                    }
    638
    639                    /**
    640                     *For messages of type POSTED_VOICE_MESSAGE, parses the body XML and creates a Voicemail instance. <br>
    641                     * @return voicemail
    642                     */
    643                    public Voicemail GetVoiceMessage() {
    644
    645                            Request request = null;
    646                            try {
    647                                    request = new XCallRequest(9,5);
    648                            } catch (IOException e) {
    649                                    e.printStackTrace();
    650                                    if (skype.errorListener != null)
    651                                            skype.errorListener.OnSkypeKitFatalError();
    652                            }
    653                            request.addParm('O',0,mObjectId);
    654
    655                            Response r = skype.XCall((XCallRequest)request);
    656
    657                            if (r.isErrCall())
    658                                    return null;
    659
    660                            int oid = 0;
    661                            Voicemail voicemail = null;
    662                            oid = r.GetOid(1);
    663                            if (oid != AbstractDecoder.NULL_VALUE) {
    664                                    voicemail = (Voicemail)skype.factory(Voicemail.moduleID(), oid, skype);
    665                            }
    666                            return voicemail;
    667                    }
    668
    669                    /**
    670                     *For messages of type POSTED_SMS, parses the body XML and creates an SMS instances <br>
    671                     * @return sms
    672                     */
    673                    public Sms GetSMS() {
    674
    675                            Request request = null;
    676                            try {
    677                                    request = new XCallRequest(9,6);
    678                            } catch (IOException e) {
    679                                    e.printStackTrace();
    680                                    if (skype.errorListener != null)
    681                                            skype.errorListener.OnSkypeKitFatalError();
    682                            }
    683                            request.addParm('O',0,mObjectId);
    684
    685                            Response r = skype.XCall((XCallRequest)request);
    686
    687                            if (r.isErrCall())
    688                                    return null;
    689
    690                            int oid = 0;
    691                            Sms sms = null;
    692                            oid = r.GetOid(1);
    693                            if (oid != AbstractDecoder.NULL_VALUE) {
    694                                    sms = (Sms)skype.factory(Sms.moduleID(), oid, skype);
    695                            }
    696                            return sms;
    697                    }
    698
    699                    /**
    700                     *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>
    701                     */
    702                    public void DeleteLocally() {
    703
    704                            Request request = null;
    705                            try {
    706                                    request = new XCallRequest(9,8);
    707                            } catch (IOException e) {
    708                                    e.printStackTrace();
    709                                    if (skype.errorListener != null)
    710                                            skype.errorListener.OnSkypeKitFatalError();
    711                            }
    712                            request.addParm('O',0,mObjectId);
    713
    714                            skype.XCall((XCallRequest)request);
    715                    }
    716
    717
    718            }