001    package com.skype.ipc;
002    
003    import java.io.IOException;
004    import java.util.ArrayList;
005    import java.util.Arrays;
006    
007    public abstract class SidObject implements SidGetResponding {
008        protected SidObject(final Integer oid, final SidRoot session, final int numProps) {
009            mSidOid       = oid;
010            mSidRoot      = session;
011            mSidTimestamp = session.mSidTimestamp;
012        }
013    
014        protected void finalize() {
015            if (mSidRoot.mSidTimestamp == mSidTimestamp)
016                mSidRoot.sidRemove(mSidOid);
017        }
018    
019        // SidGetResponding interface
020        public SidObject      sidGetObject()                                                     { return this;  }
021        public boolean        sidGetBoolProperty(final PropertyEnumConverting property)          { return false; }
022        public String         sidGetStringProperty(final PropertyEnumConverting property)        { return "";    }
023        public String         sidGetXmlProperty(final PropertyEnumConverting property)           { return sidGetStringProperty(property); }
024        public String         sidGetFilenameProperty(final PropertyEnumConverting property)      { return sidGetStringProperty(property); }
025        public int            sidGetIntProperty(final PropertyEnumConverting property)           { return 0;     }
026        public int            sidGetUintProperty(final PropertyEnumConverting property)          { return sidGetIntProperty(property); }
027        public long           sidGetLongProperty(final PropertyEnumConverting property)          { return 0;     }
028        public SidObject      sidGetObjectProperty(final PropertyEnumConverting property)        { return null;  }
029        public EnumConverting sidGetEnumProperty(final PropertyEnumConverting property)          { return null;  }
030        public byte[]         sidGetBinaryProperty(final PropertyEnumConverting property)        { return null;  }
031    
032        public void sidSetProperty(final PropertyEnumConverting property, final SidObject value) { } 
033        public void sidSetProperty(final PropertyEnumConverting property, final int value)       { }
034        public void sidSetProperty(final PropertyEnumConverting property, final String value)    { }
035        public void sidSetProperty(final PropertyEnumConverting property, final byte[] value)    { }
036        public void sidSetProperty(final PropertyEnumConverting property, final long value)      { }
037        // end SidGetResponding interface
038    
039        protected SidObject sidRequestObjectProperty(final PropertyEnumConverting property) {
040    //        int expected = 1;
041            SidObject value = null;
042            try {
043                int  oid        = 0;
044                Decoding decoder = mSidRoot.sidDoGetRequest(property.getRequest(), mSidOid);
045                while (decoder.hasNextProperty(true)) {
046                    PropertyInfo info = decoder.getNextProperty();
047                    int kind = info.kind;
048    //                assert (   expected-- > 0 
049    //                      && info.moduleId == moduleId()
050    //                      && info.propertyId == property.getId()
051    //                      && info.objectId == mSidOid
052    //                      && (kind == 'N' || kind == 'O')
053    //                     );
054                    if (kind != 'N') {
055                        oid = decoder.decodeUint();
056                    }
057                }
058                if (oid != 0) {
059                    value = mSidRoot.sidGetObject(property.getModuleId(), oid);
060                }
061                if (property.isCached()) {
062                    sidSetProperty(property, value); // synchronized
063                }
064            } catch (IOException e) {
065                mSidRoot.sidOnFatalError(e);
066            }
067            return value;
068        }
069        protected String sidRequestStringProperty(final PropertyEnumConverting property) {
070            String value = "";
071    //      int expected = 1;
072            try {
073                Decoding decoder = mSidRoot.sidDoGetRequest(property.getRequest(), mSidOid);
074                while (decoder.hasNextProperty(true)) {
075                    PropertyInfo info = decoder.getNextProperty();
076                    int kind = info.kind;
077    //              assert (   expected-- > 0 
078    //                        && info.moduleId == moduleId()
079    //                        && info.propertyId == property.getId()
080    //                        && info.objectId == mSidOid
081    //                        && (kind == 'N' || kind == 'S' || kind == 'f' || kind == 'X')
082    //                       );
083                    if (kind != 'N') {
084                        value = decoder.decodeString();
085                    }
086                }
087                if (property.isCached()) {
088                    sidSetProperty(property, value); // synchronized
089                }
090            } catch (IOException e) {
091                mSidRoot.sidOnFatalError(e);
092            }
093            return value;
094        }
095        protected byte[] sidRequestBinaryProperty(final PropertyEnumConverting property) {
096            byte[] value = null;
097            try {
098                Decoding decoder = mSidRoot.sidDoGetRequest(property.getRequest(), mSidOid);
099    //        int expected = 1;
100                while (decoder.hasNextProperty(true)) {
101                    PropertyInfo info = decoder.getNextProperty();
102                    int kind = info.kind;
103    //                assert (   expected-- > 0 
104    //                        && info.moduleId == moduleId()
105    //                        && info.propertyId == property.getId()
106    //                        && info.objectId == mSidOid
107    //                        && (kind == 'N' || kind == 'B')
108    //                       );
109                    if (kind != 'N') {
110                        value = decoder.decodeBinary();
111                    }
112                }
113                if (property.isCached()) {
114                    sidSetProperty(property, value); // synchronized
115                }
116            } catch (IOException e) {
117                mSidRoot.sidOnFatalError(e);
118            }
119            return value;
120        }
121        protected int sidRequestIntProperty(final PropertyEnumConverting property) {
122           return sidRequestProperty(property);
123        }
124        protected int sidRequestUintProperty(final PropertyEnumConverting property) {
125           return sidRequestProperty(property);
126        }
127        protected int sidRequestProperty(final PropertyEnumConverting property) {
128    //        int expected = 1;
129            int value    = 0;
130            try {
131                Decoding decoder = mSidRoot.sidDoGetRequest(property.getRequest(), mSidOid);
132                while (decoder.hasNextProperty(true)) {
133                    PropertyInfo info = decoder.getNextProperty();
134                    int kind = info.kind;
135    //                assert (   expected-- > 0 
136    //                        && info.moduleId == moduleId()
137    //                        && info.propertyId == property.getId()
138    //                        && info.objectId == mSidOid
139    //                        && (kind == 'N' || kind == 'T' || kind == 'F' || kind == 'u' || kind == 'i' || kind == 'e')
140    //                       );
141                    switch (kind) {
142                    case 'i':
143                        value = decoder.decodeInt();
144                        break;
145                    case 'u': case 'e':
146                        value = decoder.decodeUint();
147                        break;
148                    case 'T':
149                        value = 1;
150                        break;
151                    }
152                }
153                if (property.isCached()) {
154                    sidSetProperty(property, value); // synchronized
155                }
156            } catch (IOException e) {
157                mSidRoot.sidOnFatalError(e);
158            }
159            return value;
160        }
161        protected boolean sidRequestBoolProperty(final PropertyEnumConverting property) {
162            return sidRequestProperty(property) != 0;
163        }
164        protected EnumConverting sidRequestEnumProperty(final PropertyEnumConverting property) {
165            EnumConverting converter = property.getEnumConverter();
166            return converter.convert(sidRequestProperty(property));
167        }
168        protected String sidRequestXmlProperty(final PropertyEnumConverting property) {
169            return sidRequestStringProperty(property);
170        }
171        protected String sidRequestFilenameProperty(final PropertyEnumConverting property) {
172            return sidRequestStringProperty(property);
173        }
174        abstract protected void sidOnChangedProperty(final int propertyId, final int value, final String svalue);
175        abstract public int moduleId();
176        public int  getOid() {
177            return mSidOid;
178        }
179        protected Encoding sidDoRequest(final byte[] header) throws IOException {
180            return mSidRoot.sidDoRequest(header, mSidOid);
181        }
182    
183        public static SidGetResponding[] sidMultiGet(final PropertyEnumConverting[] properties, final SidObject[] objects) {
184            // normally would like to write
185            // SidGetResponding[] response = objects;
186            // which works as expected with regular java but dalvik VM raises a VerifyError...
187            SidGetResponding[] response = new SidGetResponding[objects.length];
188            boolean allCached = true;
189            for (PropertyEnumConverting p : properties) {
190                if (!p.isCached()) {
191                    allCached = false;
192                    response = new GenericGetResponse[objects.length];
193                    for (int i = 0, e = objects.length; i < e; i++) { 
194                        response[i] = new GenericGetResponse(objects[i]); 
195                    }
196                    break;
197                }
198            }
199            if (allCached) {
200                for (int i = 0, e = objects.length; i < e; i++) { 
201                    response[i] = objects[i]; 
202                }
203            }
204            return sidMultiGet(properties, objects, response); 
205        }
206    
207        /***
208         * invalidateCache: the next time the property is get, it will be querried to the runtime, meanwhile it can be discarded.
209         * This allows fine grained cache management. Note that this doesn't delete the property, you still have to set it to null
210         * to get a chance having this behavior. The rationale if that the generated properties being public, you can directly assign it to null
211         * whilst a generated invalidateCache would require switching on the values to do so. 
212         * C o; o.invalidate(C.Property.P_MY_PROP); o.mMyProp = null;
213         * @param property the property to be invalidated
214         */
215        public void invalidateCache(final PropertyEnumConverting property) {
216            int idx = property.getIdx();
217            if (idx-- > 0) {
218                mSidCached&=~(1<<(idx%32));
219            }
220        }
221    
222        protected boolean isCached(final PropertyEnumConverting property) {
223            int idx = property.getIdx();
224            if (idx-- > 0) {
225                return (mSidCached&(1<<(idx%32))) != 0;
226            }
227            return false;
228        }
229    
230        protected boolean hasCached() {
231            return mSidCached != 0;
232        }
233    
234        private PropertyInfo sidDecodeMultiGet(PropertyInfo info, final PropertyEnumConverting converter, final SidGetResponding response, final Decoding decoder) throws IOException {
235            synchronized(this) {
236                do {
237                    int kind = info.kind;
238                    PropertyEnumConverting property = (PropertyEnumConverting) converter.convert(info.propertyId); 
239                    switch (kind) {
240                    case 'i': response.sidSetProperty(property, decoder.decodeInt()); break;
241                    case 'u': case 'e': response.sidSetProperty(property, decoder.decodeUint()); break;
242                    case 'O': response.sidSetProperty(property, decoder.decodeUint()); break;
243                    case 'F': response.sidSetProperty(property, 0); break;
244                    case 'T': response.sidSetProperty(property, 1); break;
245                    case 'X': case 'S': case 'f': response.sidSetProperty(property, decoder.decodeString()); break;
246                    case 'B': response.sidSetProperty(property, decoder.decodeBinary()); break;
247                    }
248                    if (!decoder.hasNextProperty(true))
249                        return null;
250                    info = decoder.getNextProperty();
251               } while (info.objectId == mSidOid); // shall add moduleId comparison as soon as objectId definition changes 
252           }
253           return info;
254        }
255    
256        protected SidGetResponding sidMultiGet(PropertyEnumConverting[] requested) {
257            SidGetResponding response = this;
258            for (PropertyEnumConverting p : requested) {
259                if (!p.isCached()) {
260                    response = new GenericGetResponse(this);
261                    break;
262                }
263            }
264            return sidMultiGet(requested, response);
265        }
266    
267        protected SidGetResponding sidMultiGet(PropertyEnumConverting[] requested, SidGetResponding response) {
268            PropertyEnumConverting[] to_be_querried = sidComputeToBeQuerried(requested);
269            if (to_be_querried != null) {
270                try {
271                    Decoding decoder = mSidRoot.sidBeginMultiGet(to_be_querried, moduleId(), mSidOid).endMultiGet();
272                    if (decoder.hasNextProperty(true)) { // shall always be true...
273                        PropertyEnumConverting converter = to_be_querried[0];
274                        sidDecodeMultiGet(decoder.getNextProperty(), converter, response, decoder);
275                    }
276                } catch (IOException e) {
277                    mSidRoot.sidOnFatalError(e);
278                }
279            }
280            return response;
281        }
282    
283        private PropertyEnumConverting[] sidComputeToBeQuerried(PropertyEnumConverting[] requested) {
284            PropertyEnumConverting[] to_be_querried = null;
285            if (hasCached()) {
286                ArrayList<PropertyEnumConverting> missed = null; 
287                for (PropertyEnumConverting p : requested) { 
288                    if (!isCached(p)) {
289                        if (missed == null)
290                            missed = new ArrayList<PropertyEnumConverting>(requested.length);
291                        missed.add(p);
292                    }
293                }
294                if (missed != null) {
295                    to_be_querried = missed.size() == requested.length ? requested : missed.toArray(new PropertyEnumConverting[missed.size()]);
296                }
297            } else {
298                to_be_querried = requested;
299            }
300            return to_be_querried;
301        }
302    
303        protected static SidGetResponding[] sidMultiGet(PropertyEnumConverting[] requested, SidObject[] objects, SidGetResponding[] response) {
304            if (objects.length == 0 || requested.length == 0) return response;
305            PropertyEnumConverting[] previous_to_be_querried = null;
306            PropertyEnumConverting converter = requested[0];
307            Encoding encoder = null;
308            SidRoot root = objects[0].mSidRoot;
309            for (SidObject o : objects) {
310                PropertyEnumConverting[] to_be_querried = o.sidComputeToBeQuerried(requested);
311                if (to_be_querried != null) {
312                    try {
313                        if (previous_to_be_querried == null) {
314                            encoder = root.sidBeginMultiGet(to_be_querried, o.moduleId(), o.getOid());
315                        } else if (Arrays.equals(previous_to_be_querried, to_be_querried)) {
316                            encoder.addMultiGet(o.getOid());
317                            continue;
318                        } else {
319                            encoder.addMultiGet(to_be_querried, o.moduleId(), o.getOid());
320                        }
321                    } catch (IOException e) {
322                        root.sidOnFatalError(e);
323                    }
324                    previous_to_be_querried = to_be_querried;
325                }
326            }
327            if (encoder != null) {
328                try {
329                    Decoding decoder = encoder.endMultiGet();
330                    int r = 0;
331                    if (decoder.hasNextProperty(true)) { // shall always be true...
332                        PropertyInfo info = decoder.getNextProperty();
333                        do {
334                           SidObject o = response[r].sidGetObject(); 
335                           while (o.getOid() != info.objectId) {
336                              o = response[++r].sidGetObject(); // some responses may already be processed.
337                           }
338                           info = o.sidDecodeMultiGet(info, converter, response[r++], decoder);
339                        } while (info != null);
340                    }
341                } catch (IOException e) { // catch NullPointerException?
342                    root.sidOnFatalError(e);
343                }
344            }
345            return response;
346        }
347    
348        protected int     mSidCached;
349        protected int     mSidOid;
350        protected SidRoot mSidRoot;
351        protected int     mSidTimestamp;
352    };
353