1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*jshint evil:true*/
21
22/**
23 * The Thrift namespace houses the Apache Thrift JavaScript library
24 * elements providing JavaScript bindings for the Apache Thrift RPC
25 * system. End users will typically only directly make use of the
26 * Transport (TXHRTransport/TWebSocketTransport) and Protocol
27 * (TJSONPRotocol/TBinaryProtocol) constructors.
28 *
29 * Object methods beginning with a __ (e.g. __onOpen()) are internal
30 * and should not be called outside of the object's own methods.
31 *
32 * This library creates one global object: Thrift
33 * Code in this library must never create additional global identifiers,
34 * all features must be scoped within the Thrift namespace.
35 * @namespace
36 * @example
37 *     var transport = new Thrift.Transport('http://localhost:8585');
38 *     var protocol  = new Thrift.Protocol(transport);
39 *     var client = new MyThriftSvcClient(protocol);
40 *     var result = client.MyMethod();
41 */
42var Thrift = {
43    /**
44     * Thrift JavaScript library version.
45     * @readonly
46     * @const {string} Version
47     * @memberof Thrift
48     */
49    Version: '0.18.0',
50
51    /**
52     * Thrift IDL type string to Id mapping.
53     * @readonly
54     * @property {number}  STOP   - End of a set of fields.
55     * @property {number}  VOID   - No value (only legal for return types).
56     * @property {number}  BOOL   - True/False integer.
57     * @property {number}  BYTE   - Signed 8 bit integer.
58     * @property {number}  I08    - Signed 8 bit integer.
59     * @property {number}  DOUBLE - 64 bit IEEE 854 floating point.
60     * @property {number}  I16    - Signed 16 bit integer.
61     * @property {number}  I32    - Signed 32 bit integer.
62     * @property {number}  I64    - Signed 64 bit integer.
63     * @property {number}  STRING - Array of bytes representing a string of characters.
64     * @property {number}  UTF7   - Array of bytes representing a string of UTF7 encoded characters.
65     * @property {number}  STRUCT - A multifield type.
66     * @property {number}  MAP    - A collection type (map/associative-array/dictionary).
67     * @property {number}  SET    - A collection type (unordered and without repeated values).
68     * @property {number}  LIST   - A collection type (unordered).
69     * @property {number}  UTF8   - Array of bytes representing a string of UTF8 encoded characters.
70     * @property {number}  UTF16  - Array of bytes representing a string of UTF16 encoded characters.
71     */
72    Type: {
73        STOP: 0,
74        VOID: 1,
75        BOOL: 2,
76        BYTE: 3,
77        I08: 3,
78        DOUBLE: 4,
79        I16: 6,
80        I32: 8,
81        I64: 10,
82        STRING: 11,
83        UTF7: 11,
84        STRUCT: 12,
85        MAP: 13,
86        SET: 14,
87        LIST: 15,
88        UTF8: 16,
89        UTF16: 17
90    },
91
92    /**
93     * Thrift RPC message type string to Id mapping.
94     * @readonly
95     * @property {number}  CALL      - RPC call sent from client to server.
96     * @property {number}  REPLY     - RPC call normal response from server to client.
97     * @property {number}  EXCEPTION - RPC call exception response from server to client.
98     * @property {number}  ONEWAY    - Oneway RPC call from client to server with no response.
99     */
100    MessageType: {
101        CALL: 1,
102        REPLY: 2,
103        EXCEPTION: 3,
104        ONEWAY: 4
105    },
106
107    /**
108     * Utility function returning the count of an object's own properties.
109     * @param {object} obj - Object to test.
110     * @returns {number} number of object's own properties
111     */
112    objectLength: function(obj) {
113        var length = 0;
114        for (var k in obj) {
115            if (obj.hasOwnProperty(k)) {
116                length++;
117            }
118        }
119        return length;
120    },
121
122    /**
123     * Utility function to establish prototype inheritance.
124     * @see {@link http://javascript.crockford.com/prototypal.html|Prototypal Inheritance}
125     * @param {function} constructor - Contstructor function to set as derived.
126     * @param {function} superConstructor - Contstructor function to set as base.
127     * @param {string} [name] - Type name to set as name property in derived prototype.
128     */
129    inherits: function(constructor, superConstructor, name) {
130      function F() {}
131      F.prototype = superConstructor.prototype;
132      constructor.prototype = new F();
133      constructor.prototype.name = name || '';
134    }
135};
136
137/**
138 * Initializes a Thrift TException instance.
139 * @constructor
140 * @augments Error
141 * @param {string} message - The TException message (distinct from the Error message).
142 * @classdesc TException is the base class for all Thrift exceptions types.
143 */
144Thrift.TException = function(message) {
145    this.message = message;
146};
147Thrift.inherits(Thrift.TException, Error, 'TException');
148
149/**
150 * Returns the message set on the exception.
151 * @readonly
152 * @returns {string} exception message
153 */
154Thrift.TException.prototype.getMessage = function() {
155    return this.message;
156};
157
158/**
159 * Thrift Application Exception type string to Id mapping.
160 * @readonly
161 * @property {number}  UNKNOWN                 - Unknown/undefined.
162 * @property {number}  UNKNOWN_METHOD          - Client attempted to call a method unknown to the server.
163 * @property {number}  INVALID_MESSAGE_TYPE    - Client passed an unknown/unsupported MessageType.
164 * @property {number}  WRONG_METHOD_NAME       - Unused.
165 * @property {number}  BAD_SEQUENCE_ID         - Unused in Thrift RPC, used to flag proprietary sequence number errors.
166 * @property {number}  MISSING_RESULT          - Raised by a server processor if a handler fails to supply the required return result.
167 * @property {number}  INTERNAL_ERROR          - Something bad happened.
168 * @property {number}  PROTOCOL_ERROR          - The protocol layer failed to serialize or deserialize data.
169 * @property {number}  INVALID_TRANSFORM       - Unused.
170 * @property {number}  INVALID_PROTOCOL        - The protocol (or version) is not supported.
171 * @property {number}  UNSUPPORTED_CLIENT_TYPE - Unused.
172 */
173Thrift.TApplicationExceptionType = {
174    UNKNOWN: 0,
175    UNKNOWN_METHOD: 1,
176    INVALID_MESSAGE_TYPE: 2,
177    WRONG_METHOD_NAME: 3,
178    BAD_SEQUENCE_ID: 4,
179    MISSING_RESULT: 5,
180    INTERNAL_ERROR: 6,
181    PROTOCOL_ERROR: 7,
182    INVALID_TRANSFORM: 8,
183    INVALID_PROTOCOL: 9,
184    UNSUPPORTED_CLIENT_TYPE: 10
185};
186
187/**
188 * Initializes a Thrift TApplicationException instance.
189 * @constructor
190 * @augments Thrift.TException
191 * @param {string} message - The TApplicationException message (distinct from the Error message).
192 * @param {Thrift.TApplicationExceptionType} [code] - The TApplicationExceptionType code.
193 * @classdesc TApplicationException is the exception class used to propagate exceptions from an RPC server back to a calling client.
194*/
195Thrift.TApplicationException = function(message, code) {
196    this.message = message;
197    this.code = typeof code === 'number' ? code : 0;
198};
199Thrift.inherits(Thrift.TApplicationException, Thrift.TException, 'TApplicationException');
200
201/**
202 * Read a TApplicationException from the supplied protocol.
203 * @param {object} input - The input protocol to read from.
204 */
205Thrift.TApplicationException.prototype.read = function(input) {
206    while (1) {
207        var ret = input.readFieldBegin();
208
209        if (ret.ftype == Thrift.Type.STOP) {
210            break;
211        }
212
213        var fid = ret.fid;
214
215        switch (fid) {
216            case 1:
217                if (ret.ftype == Thrift.Type.STRING) {
218                    ret = input.readString();
219                    this.message = ret.value;
220                } else {
221                    ret = input.skip(ret.ftype);
222                }
223                break;
224            case 2:
225                if (ret.ftype == Thrift.Type.I32) {
226                    ret = input.readI32();
227                    this.code = ret.value;
228                } else {
229                    ret = input.skip(ret.ftype);
230                }
231                break;
232           default:
233                ret = input.skip(ret.ftype);
234                break;
235        }
236
237        input.readFieldEnd();
238    }
239
240    input.readStructEnd();
241};
242
243/**
244 * Wite a TApplicationException to the supplied protocol.
245 * @param {object} output - The output protocol to write to.
246 */
247Thrift.TApplicationException.prototype.write = function(output) {
248    output.writeStructBegin('TApplicationException');
249
250    if (this.message) {
251        output.writeFieldBegin('message', Thrift.Type.STRING, 1);
252        output.writeString(this.getMessage());
253        output.writeFieldEnd();
254    }
255
256    if (this.code) {
257        output.writeFieldBegin('type', Thrift.Type.I32, 2);
258        output.writeI32(this.code);
259        output.writeFieldEnd();
260    }
261
262    output.writeFieldStop();
263    output.writeStructEnd();
264};
265
266/**
267 * Returns the application exception code set on the exception.
268 * @readonly
269 * @returns {Thrift.TApplicationExceptionType} exception code
270 */
271Thrift.TApplicationException.prototype.getCode = function() {
272    return this.code;
273};
274
275Thrift.TProtocolExceptionType = {
276    UNKNOWN: 0,
277    INVALID_DATA: 1,
278    NEGATIVE_SIZE: 2,
279    SIZE_LIMIT: 3,
280    BAD_VERSION: 4,
281    NOT_IMPLEMENTED: 5,
282    DEPTH_LIMIT: 6
283};
284
285Thrift.TProtocolException = function TProtocolException(type, message) {
286    Error.call(this);
287    if (Error.captureStackTrace !== undefined) {
288        Error.captureStackTrace(this, this.constructor);
289    }
290    this.name = this.constructor.name;
291    this.type = type;
292    this.message = message;
293};
294Thrift.inherits(Thrift.TProtocolException, Thrift.TException, 'TProtocolException');
295
296/**
297 * Constructor Function for the XHR transport.
298 * If you do not specify a url then you must handle XHR operations on
299 * your own. This type can also be constructed using the Transport alias
300 * for backward compatibility.
301 * @constructor
302 * @param {string} [url] - The URL to connect to.
303 * @classdesc The Apache Thrift Transport layer performs byte level I/O
304 * between RPC clients and servers. The JavaScript TXHRTransport object
305 * uses Http[s]/XHR. Target servers must implement the http[s] transport
306 * (see: node.js example server_http.js).
307 * @example
308 *     var transport = new Thrift.TXHRTransport("http://localhost:8585");
309 */
310Thrift.Transport = Thrift.TXHRTransport = function(url, options) {
311    this.url = url;
312    this.wpos = 0;
313    this.rpos = 0;
314    this.useCORS = (options && options.useCORS);
315    this.customHeaders = options ? (options.customHeaders ? options.customHeaders : {}): {};
316    this.send_buf = '';
317    this.recv_buf = '';
318};
319
320Thrift.TXHRTransport.prototype = {
321    /**
322     * Gets the browser specific XmlHttpRequest Object.
323     * @returns {object} the browser XHR interface object
324     */
325    getXmlHttpRequestObject: function() {
326        try { return new XMLHttpRequest(); } catch (e1) { }
327        try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { }
328        try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { }
329
330        throw "Your browser doesn't support XHR.";
331    },
332
333    /**
334     * Sends the current XRH request if the transport was created with a URL
335     * and the async parameter is false. If the transport was not created with
336     * a URL, or the async parameter is True and no callback is provided, or
337     * the URL is an empty string, the current send buffer is returned.
338     * @param {object} async - If true the current send buffer is returned.
339     * @param {object} callback - Optional async completion callback
340     * @returns {undefined|string} Nothing or the current send buffer.
341     * @throws {string} If XHR fails.
342     */
343    flush: function(async, callback) {
344        var self = this;
345        if ((async && !callback) || this.url === undefined || this.url === '') {
346            return this.send_buf;
347        }
348
349        var xreq = this.getXmlHttpRequestObject();
350
351        if (xreq.overrideMimeType) {
352            xreq.overrideMimeType('application/vnd.apache.thrift.json; charset=utf-8');
353        }
354
355        if (callback) {
356            //Ignore XHR callbacks until the data arrives, then call the
357            //  client's callback
358            xreq.onreadystatechange =
359              (function() {
360                var clientCallback = callback;
361                return function() {
362                  if (this.readyState == 4 && this.status == 200) {
363                    self.setRecvBuffer(this.responseText);
364                    clientCallback();
365                  }
366                };
367              }());
368
369            // detect net::ERR_CONNECTION_REFUSED and call the callback.
370            xreq.onerror =
371                (function() {
372                  var clientCallback = callback;
373                  return function() {
374                      clientCallback();
375                  };
376                }());
377
378        }
379
380        xreq.open('POST', this.url, !!async);
381
382        // add custom headers
383        Object.keys(self.customHeaders).forEach(function(prop) {
384            xreq.setRequestHeader(prop, self.customHeaders[prop]);
385        });
386
387        if (xreq.setRequestHeader) {
388            xreq.setRequestHeader('Accept', 'application/vnd.apache.thrift.json; charset=utf-8');
389            xreq.setRequestHeader('Content-Type', 'application/vnd.apache.thrift.json; charset=utf-8');
390        }
391
392        xreq.send(this.send_buf);
393        if (async && callback) {
394            return;
395        }
396
397        if (xreq.readyState != 4) {
398            throw 'encountered an unknown ajax ready state: ' + xreq.readyState;
399        }
400
401        if (xreq.status != 200) {
402            throw 'encountered a unknown request status: ' + xreq.status;
403        }
404
405        this.recv_buf = xreq.responseText;
406        this.recv_buf_sz = this.recv_buf.length;
407        this.wpos = this.recv_buf.length;
408        this.rpos = 0;
409    },
410
411    /**
412     * Creates a jQuery XHR object to be used for a Thrift server call.
413     * @param {object} client - The Thrift Service client object generated by the IDL compiler.
414     * @param {object} postData - The message to send to the server.
415     * @param {function} args - The original call arguments with the success call back at the end.
416     * @param {function} recv_method - The Thrift Service Client receive method for the call.
417     * @returns {object} A new jQuery XHR object.
418     * @throws {string} If the jQuery version is prior to 1.5 or if jQuery is not found.
419     */
420    jqRequest: function(client, postData, args, recv_method) {
421        if (typeof jQuery === 'undefined' ||
422            typeof jQuery.Deferred === 'undefined') {
423            throw 'Thrift.js requires jQuery 1.5+ to use asynchronous requests';
424        }
425
426        var thriftTransport = this;
427
428        var jqXHR = jQuery.ajax({
429            url: this.url,
430            data: postData,
431            type: 'POST',
432            cache: false,
433            contentType: 'application/vnd.apache.thrift.json; charset=utf-8',
434            dataType: 'text thrift',
435            converters: {
436                'text thrift' : function(responseData) {
437                    thriftTransport.setRecvBuffer(responseData);
438                    var value = recv_method.call(client);
439                    return value;
440                }
441            },
442            context: client,
443            success: jQuery.makeArray(args).pop(),
444            beforeSend: function (xreq) {
445                Object.keys(thriftTransport.customHeaders).forEach(function (prop) {
446                    xreq.setRequestHeader(prop, thriftTransport.customHeaders[prop]);
447                });
448            }
449        });
450
451        return jqXHR;
452    },
453
454    /**
455     * Sets the buffer to provide the protocol when deserializing.
456     * @param {string} buf - The buffer to supply the protocol.
457     */
458    setRecvBuffer: function(buf) {
459        this.recv_buf = buf;
460        this.recv_buf_sz = this.recv_buf.length;
461        this.wpos = this.recv_buf.length;
462        this.rpos = 0;
463    },
464
465    /**
466     * Returns true if the transport is open, XHR always returns true.
467     * @readonly
468     * @returns {boolean} Always True.
469     */
470    isOpen: function() {
471        return true;
472    },
473
474    /**
475     * Opens the transport connection, with XHR this is a nop.
476     */
477    open: function() {},
478
479    /**
480     * Closes the transport connection, with XHR this is a nop.
481     */
482    close: function() {},
483
484    /**
485     * Returns the specified number of characters from the response
486     * buffer.
487     * @param {number} len - The number of characters to return.
488     * @returns {string} Characters sent by the server.
489     */
490    read: function(len) {
491        var avail = this.wpos - this.rpos;
492
493        if (avail === 0) {
494            return '';
495        }
496
497        var give = len;
498
499        if (avail < len) {
500            give = avail;
501        }
502
503        var ret = this.read_buf.substr(this.rpos, give);
504        this.rpos += give;
505
506        //clear buf when complete?
507        return ret;
508    },
509
510    /**
511     * Returns the entire response buffer.
512     * @returns {string} Characters sent by the server.
513     */
514    readAll: function() {
515        return this.recv_buf;
516    },
517
518    /**
519     * Sets the send buffer to buf.
520     * @param {string} buf - The buffer to send.
521     */
522    write: function(buf) {
523        this.send_buf = buf;
524    },
525
526    /**
527     * Returns the send buffer.
528     * @readonly
529     * @returns {string} The send buffer.
530     */
531    getSendBuffer: function() {
532        return this.send_buf;
533    }
534
535};
536
537
538/**
539 * Constructor Function for the WebSocket transport.
540 * @constructor
541 * @param {string} [url] - The URL to connect to.
542 * @classdesc The Apache Thrift Transport layer performs byte level I/O
543 * between RPC clients and servers. The JavaScript TWebSocketTransport object
544 * uses the WebSocket protocol. Target servers must implement WebSocket.
545 * (see: node.js example server_http.js).
546 * @example
547 *   var transport = new Thrift.TWebSocketTransport("http://localhost:8585");
548 */
549Thrift.TWebSocketTransport = function(url) {
550    this.__reset(url);
551};
552
553Thrift.TWebSocketTransport.prototype = {
554    __reset: function(url) {
555      this.url = url;             //Where to connect
556      this.socket = null;         //The web socket
557      this.callbacks = [];        //Pending callbacks
558      this.send_pending = [];     //Buffers/Callback pairs waiting to be sent
559      this.send_buf = '';         //Outbound data, immutable until sent
560      this.recv_buf = '';         //Inbound data
561      this.rb_wpos = 0;           //Network write position in receive buffer
562      this.rb_rpos = 0;           //Client read position in receive buffer
563    },
564
565    /**
566     * Sends the current WS request and registers callback. The async
567     * parameter is ignored (WS flush is always async) and the callback
568     * function parameter is required.
569     * @param {object} async - Ignored.
570     * @param {object} callback - The client completion callback.
571     * @returns {undefined|string} Nothing (undefined)
572     */
573    flush: function(async, callback) {
574      var self = this;
575      if (this.isOpen()) {
576        //Send data and register a callback to invoke the client callback
577        this.socket.send(this.send_buf);
578        this.callbacks.push((function() {
579          var clientCallback = callback;
580          return function(msg) {
581            self.setRecvBuffer(msg);
582            if (clientCallback) {
583                clientCallback();
584            }
585          };
586        }()));
587      } else {
588        //Queue the send to go out __onOpen
589        this.send_pending.push({
590          buf: this.send_buf,
591          cb: callback
592        });
593      }
594    },
595
596    __onOpen: function() {
597       var self = this;
598       if (this.send_pending.length > 0) {
599          //If the user made calls before the connection was fully
600          //open, send them now
601          this.send_pending.forEach(function(elem) {
602             self.socket.send(elem.buf);
603             self.callbacks.push((function() {
604               var clientCallback = elem.cb;
605               return function(msg) {
606                  self.setRecvBuffer(msg);
607                  clientCallback();
608               };
609             }()));
610          });
611          this.send_pending = [];
612       }
613    },
614
615    __onClose: function(evt) {
616      this.__reset(this.url);
617    },
618
619    __onMessage: function(evt) {
620      if (this.callbacks.length) {
621        this.callbacks.shift()(evt.data);
622      }
623    },
624
625    __onError: function(evt) {
626      console.log('Thrift WebSocket Error: ' + evt.toString());
627      this.socket.close();
628    },
629
630    /**
631     * Sets the buffer to use when receiving server responses.
632     * @param {string} buf - The buffer to receive server responses.
633     */
634    setRecvBuffer: function(buf) {
635        this.recv_buf = buf;
636        this.recv_buf_sz = this.recv_buf.length;
637        this.wpos = this.recv_buf.length;
638        this.rpos = 0;
639    },
640
641    /**
642     * Returns true if the transport is open
643     * @readonly
644     * @returns {boolean}
645     */
646    isOpen: function() {
647        return this.socket && this.socket.readyState == this.socket.OPEN;
648    },
649
650    /**
651     * Opens the transport connection
652     */
653    open: function() {
654      //If OPEN/CONNECTING/CLOSING ignore additional opens
655      if (this.socket && this.socket.readyState != this.socket.CLOSED) {
656        return;
657      }
658      //If there is no socket or the socket is closed:
659      this.socket = new WebSocket(this.url);
660      this.socket.onopen = this.__onOpen.bind(this);
661      this.socket.onmessage = this.__onMessage.bind(this);
662      this.socket.onerror = this.__onError.bind(this);
663      this.socket.onclose = this.__onClose.bind(this);
664    },
665
666    /**
667     * Closes the transport connection
668     */
669    close: function() {
670      this.socket.close();
671    },
672
673    /**
674     * Returns the specified number of characters from the response
675     * buffer.
676     * @param {number} len - The number of characters to return.
677     * @returns {string} Characters sent by the server.
678     */
679    read: function(len) {
680        var avail = this.wpos - this.rpos;
681
682        if (avail === 0) {
683            return '';
684        }
685
686        var give = len;
687
688        if (avail < len) {
689            give = avail;
690        }
691
692        var ret = this.read_buf.substr(this.rpos, give);
693        this.rpos += give;
694
695        //clear buf when complete?
696        return ret;
697    },
698
699    /**
700     * Returns the entire response buffer.
701     * @returns {string} Characters sent by the server.
702     */
703    readAll: function() {
704        return this.recv_buf;
705    },
706
707    /**
708     * Sets the send buffer to buf.
709     * @param {string} buf - The buffer to send.
710     */
711    write: function(buf) {
712        this.send_buf = buf;
713    },
714
715    /**
716     * Returns the send buffer.
717     * @readonly
718     * @returns {string} The send buffer.
719     */
720    getSendBuffer: function() {
721        return this.send_buf;
722    }
723
724};
725
726/**
727 * Initializes a Thrift JSON protocol instance.
728 * @constructor
729 * @param {Thrift.Transport} transport - The transport to serialize to/from.
730 * @classdesc Apache Thrift Protocols perform serialization which enables cross
731 * language RPC. The Protocol type is the JavaScript browser implementation
732 * of the Apache Thrift TJSONProtocol.
733 * @example
734 *     var protocol  = new Thrift.Protocol(transport);
735 */
736Thrift.TJSONProtocol = Thrift.Protocol = function(transport) {
737    this.tstack = [];
738    this.tpos = [];
739    this.transport = transport;
740};
741
742/**
743 * Thrift IDL type Id to string mapping.
744 * @readonly
745 * @see {@link Thrift.Type}
746 */
747Thrift.Protocol.Type = {};
748Thrift.Protocol.Type[Thrift.Type.BOOL] = '"tf"';
749Thrift.Protocol.Type[Thrift.Type.BYTE] = '"i8"';
750Thrift.Protocol.Type[Thrift.Type.I16] = '"i16"';
751Thrift.Protocol.Type[Thrift.Type.I32] = '"i32"';
752Thrift.Protocol.Type[Thrift.Type.I64] = '"i64"';
753Thrift.Protocol.Type[Thrift.Type.DOUBLE] = '"dbl"';
754Thrift.Protocol.Type[Thrift.Type.STRUCT] = '"rec"';
755Thrift.Protocol.Type[Thrift.Type.STRING] = '"str"';
756Thrift.Protocol.Type[Thrift.Type.MAP] = '"map"';
757Thrift.Protocol.Type[Thrift.Type.LIST] = '"lst"';
758Thrift.Protocol.Type[Thrift.Type.SET] = '"set"';
759
760/**
761 * Thrift IDL type string to Id mapping.
762 * @readonly
763 * @see {@link Thrift.Type}
764 */
765Thrift.Protocol.RType = {};
766Thrift.Protocol.RType.tf = Thrift.Type.BOOL;
767Thrift.Protocol.RType.i8 = Thrift.Type.BYTE;
768Thrift.Protocol.RType.i16 = Thrift.Type.I16;
769Thrift.Protocol.RType.i32 = Thrift.Type.I32;
770Thrift.Protocol.RType.i64 = Thrift.Type.I64;
771Thrift.Protocol.RType.dbl = Thrift.Type.DOUBLE;
772Thrift.Protocol.RType.rec = Thrift.Type.STRUCT;
773Thrift.Protocol.RType.str = Thrift.Type.STRING;
774Thrift.Protocol.RType.map = Thrift.Type.MAP;
775Thrift.Protocol.RType.lst = Thrift.Type.LIST;
776Thrift.Protocol.RType.set = Thrift.Type.SET;
777
778/**
779 * The TJSONProtocol version number.
780 * @readonly
781 * @const {number} Version
782 * @memberof Thrift.Protocol
783 */
784 Thrift.Protocol.Version = 1;
785
786Thrift.Protocol.prototype = {
787    /**
788     * Returns the underlying transport.
789     * @readonly
790     * @returns {Thrift.Transport} The underlying transport.
791     */
792    getTransport: function() {
793        return this.transport;
794    },
795
796    /**
797     * Serializes the beginning of a Thrift RPC message.
798     * @param {string} name - The service method to call.
799     * @param {Thrift.MessageType} messageType - The type of method call.
800     * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
801     */
802    writeMessageBegin: function(name, messageType, seqid) {
803        this.tstack = [];
804        this.tpos = [];
805
806        this.tstack.push([Thrift.Protocol.Version, '"' +
807            name + '"', messageType, seqid]);
808    },
809
810    /**
811     * Serializes the end of a Thrift RPC message.
812     */
813    writeMessageEnd: function() {
814        var obj = this.tstack.pop();
815
816        this.wobj = this.tstack.pop();
817        this.wobj.push(obj);
818
819        this.wbuf = '[' + this.wobj.join(',') + ']';
820
821        this.transport.write(this.wbuf);
822     },
823
824
825    /**
826     * Serializes the beginning of a struct.
827     * @param {string} name - The name of the struct.
828     */
829    writeStructBegin: function(name) {
830        this.tpos.push(this.tstack.length);
831        this.tstack.push({});
832    },
833
834    /**
835     * Serializes the end of a struct.
836     */
837    writeStructEnd: function() {
838
839        var p = this.tpos.pop();
840        var struct = this.tstack[p];
841        var str = '{';
842        var first = true;
843        for (var key in struct) {
844            if (first) {
845                first = false;
846            } else {
847                str += ',';
848            }
849
850            str += key + ':' + struct[key];
851        }
852
853        str += '}';
854        this.tstack[p] = str;
855    },
856
857    /**
858     * Serializes the beginning of a struct field.
859     * @param {string} name - The name of the field.
860     * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
861     * @param {number} fieldId - The field's unique identifier.
862     */
863    writeFieldBegin: function(name, fieldType, fieldId) {
864        this.tpos.push(this.tstack.length);
865        this.tstack.push({ 'fieldId': '"' +
866            fieldId + '"', 'fieldType': Thrift.Protocol.Type[fieldType]
867        });
868
869    },
870
871    /**
872     * Serializes the end of a field.
873     */
874    writeFieldEnd: function() {
875        var value = this.tstack.pop();
876        var fieldInfo = this.tstack.pop();
877
878        this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
879            fieldInfo.fieldType + ':' + value + '}';
880        this.tpos.pop();
881    },
882
883    /**
884     * Serializes the end of the set of fields for a struct.
885     */
886    writeFieldStop: function() {
887        //na
888    },
889
890    /**
891     * Serializes the beginning of a map collection.
892     * @param {Thrift.Type} keyType - The data type of the key.
893     * @param {Thrift.Type} valType - The data type of the value.
894     * @param {number} [size] - The number of elements in the map (ignored).
895     */
896    writeMapBegin: function(keyType, valType, size) {
897        this.tpos.push(this.tstack.length);
898        this.tstack.push([Thrift.Protocol.Type[keyType],
899            Thrift.Protocol.Type[valType], 0]);
900    },
901
902    /**
903     * Serializes the end of a map.
904     */
905    writeMapEnd: function() {
906        var p = this.tpos.pop();
907
908        if (p == this.tstack.length) {
909            return;
910        }
911
912        if ((this.tstack.length - p - 1) % 2 !== 0) {
913            this.tstack.push('');
914        }
915
916        var size = (this.tstack.length - p - 1) / 2;
917
918        this.tstack[p][this.tstack[p].length - 1] = size;
919
920        var map = '}';
921        var first = true;
922        while (this.tstack.length > p + 1) {
923            var v = this.tstack.pop();
924            var k = this.tstack.pop();
925            if (first) {
926                first = false;
927            } else {
928                map = ',' + map;
929            }
930
931            if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
932            map = k + ':' + v + map;
933        }
934        map = '{' + map;
935
936        this.tstack[p].push(map);
937        this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
938    },
939
940    /**
941     * Serializes the beginning of a list collection.
942     * @param {Thrift.Type} elemType - The data type of the elements.
943     * @param {number} size - The number of elements in the list.
944     */
945    writeListBegin: function(elemType, size) {
946        this.tpos.push(this.tstack.length);
947        this.tstack.push([Thrift.Protocol.Type[elemType], size]);
948    },
949
950    /**
951     * Serializes the end of a list.
952     */
953    writeListEnd: function() {
954        var p = this.tpos.pop();
955
956        while (this.tstack.length > p + 1) {
957            var tmpVal = this.tstack[p + 1];
958            this.tstack.splice(p + 1, 1);
959            this.tstack[p].push(tmpVal);
960        }
961
962        this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
963    },
964
965    /**
966     * Serializes the beginning of a set collection.
967     * @param {Thrift.Type} elemType - The data type of the elements.
968     * @param {number} size - The number of elements in the list.
969     */
970    writeSetBegin: function(elemType, size) {
971        this.tpos.push(this.tstack.length);
972        this.tstack.push([Thrift.Protocol.Type[elemType], size]);
973    },
974
975    /**
976     * Serializes the end of a set.
977     */
978    writeSetEnd: function() {
979        var p = this.tpos.pop();
980
981        while (this.tstack.length > p + 1) {
982            var tmpVal = this.tstack[p + 1];
983            this.tstack.splice(p + 1, 1);
984            this.tstack[p].push(tmpVal);
985        }
986
987        this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
988    },
989
990    /** Serializes a boolean */
991    writeBool: function(value) {
992        this.tstack.push(value ? 1 : 0);
993    },
994
995    /** Serializes a number */
996    writeByte: function(i8) {
997        this.tstack.push(i8);
998    },
999
1000    /** Serializes a number */
1001    writeI16: function(i16) {
1002        this.tstack.push(i16);
1003    },
1004
1005    /** Serializes a number */
1006    writeI32: function(i32) {
1007        this.tstack.push(i32);
1008    },
1009
1010    /** Serializes a number */
1011    writeI64: function(i64) {
1012        if (typeof i64 === 'number') {
1013            this.tstack.push(i64);
1014        } else {
1015            this.tstack.push(Int64Util.toDecimalString(i64));
1016        }
1017    },
1018
1019    /** Serializes a number */
1020    writeDouble: function(dbl) {
1021        this.tstack.push(dbl);
1022    },
1023
1024    /** Serializes a string */
1025    writeString: function(str) {
1026        // We do not encode uri components for wire transfer:
1027        if (str === null) {
1028            this.tstack.push(null);
1029        } else {
1030            // concat may be slower than building a byte buffer
1031            var escapedString = '';
1032            for (var i = 0; i < str.length; i++) {
1033                var ch = str.charAt(i);      // a single double quote: "
1034                if (ch === '\"') {
1035                    escapedString += '\\\"'; // write out as: \"
1036                } else if (ch === '\\') {    // a single backslash
1037                    escapedString += '\\\\'; // write out as double backslash
1038                } else if (ch === '\b') {    // a single backspace: invisible
1039                    escapedString += '\\b';  // write out as: \b"
1040                } else if (ch === '\f') {    // a single formfeed: invisible
1041                    escapedString += '\\f';  // write out as: \f"
1042                } else if (ch === '\n') {    // a single newline: invisible
1043                    escapedString += '\\n';  // write out as: \n"
1044                } else if (ch === '\r') {    // a single return: invisible
1045                    escapedString += '\\r';  // write out as: \r"
1046                } else if (ch === '\t') {    // a single tab: invisible
1047                    escapedString += '\\t';  // write out as: \t"
1048                } else {
1049                    escapedString += ch;     // Else it need not be escaped
1050                }
1051            }
1052            this.tstack.push('"' + escapedString + '"');
1053        }
1054    },
1055
1056    /** Serializes a string */
1057    writeBinary: function(binary) {
1058        var str = '';
1059        if (typeof binary == 'string') {
1060            str = binary;
1061        } else if (binary instanceof Uint8Array) {
1062            var arr = binary;
1063            for (var i = 0; i < arr.length; ++i) {
1064                str += String.fromCharCode(arr[i]);
1065            }
1066        } else {
1067            throw new TypeError('writeBinary only accepts String or Uint8Array.');
1068        }
1069        this.tstack.push('"' + btoa(str) + '"');
1070    },
1071
1072    /**
1073       @class
1074       @name AnonReadMessageBeginReturn
1075       @property {string} fname - The name of the service method.
1076       @property {Thrift.MessageType} mtype - The type of message call.
1077       @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
1078     */
1079    /**
1080     * Deserializes the beginning of a message.
1081     * @returns {AnonReadMessageBeginReturn}
1082     */
1083    readMessageBegin: function() {
1084        this.rstack = [];
1085        this.rpos = [];
1086
1087        received = this.transport.readAll();
1088
1089        if (typeof JSONInt64 !== 'undefined' && typeof JSONInt64.parse === 'function') {
1090            this.robj = JSONInt64.parse(received);
1091        } else if (typeof JSON !== 'undefined' && typeof JSON.parse === 'function') {
1092            this.robj = JSON.parse(received);
1093        } else if (typeof jQuery !== 'undefined') {
1094            this.robj = jQuery.parseJSON(received);
1095        } else {
1096            this.robj = eval(received);
1097        }
1098
1099        var r = {};
1100        var version = this.robj.shift();
1101
1102        if (version != Thrift.Protocol.Version) {
1103            throw 'Wrong thrift protocol version: ' + version;
1104        }
1105
1106        r.fname = this.robj.shift();
1107        r.mtype = this.robj.shift();
1108        r.rseqid = this.robj.shift();
1109
1110
1111        //get to the main obj
1112        this.rstack.push(this.robj.shift());
1113
1114        return r;
1115    },
1116
1117    /** Deserializes the end of a message. */
1118    readMessageEnd: function() {
1119    },
1120
1121    /**
1122     * Deserializes the beginning of a struct.
1123     * @param {string} [name] - The name of the struct (ignored)
1124     * @returns {object} - An object with an empty string fname property
1125     */
1126    readStructBegin: function(name) {
1127        var r = {};
1128        r.fname = '';
1129
1130        //incase this is an array of structs
1131        if (this.rstack[this.rstack.length - 1] instanceof Array) {
1132            this.rstack.push(this.rstack[this.rstack.length - 1].shift());
1133        }
1134
1135        return r;
1136    },
1137
1138    /** Deserializes the end of a struct. */
1139    readStructEnd: function() {
1140        if (this.rstack[this.rstack.length - 2] instanceof Array) {
1141            this.rstack.pop();
1142        }
1143    },
1144
1145    /**
1146       @class
1147       @name AnonReadFieldBeginReturn
1148       @property {string} fname - The name of the field (always '').
1149       @property {Thrift.Type} ftype - The data type of the field.
1150       @property {number} fid - The unique identifier of the field.
1151     */
1152    /**
1153     * Deserializes the beginning of a field.
1154     * @returns {AnonReadFieldBeginReturn}
1155     */
1156    readFieldBegin: function() {
1157        var r = {};
1158
1159        var fid = -1;
1160        var ftype = Thrift.Type.STOP;
1161
1162        //get a fieldId
1163        for (var f in (this.rstack[this.rstack.length - 1])) {
1164            if (f === null) {
1165              continue;
1166            }
1167
1168            fid = parseInt(f, 10);
1169            this.rpos.push(this.rstack.length);
1170
1171            var field = this.rstack[this.rstack.length - 1][fid];
1172
1173            //remove so we don't see it again
1174            delete this.rstack[this.rstack.length - 1][fid];
1175
1176            this.rstack.push(field);
1177
1178            break;
1179        }
1180
1181        if (fid != -1) {
1182
1183            //should only be 1 of these but this is the only
1184            //way to match a key
1185            for (var i in (this.rstack[this.rstack.length - 1])) {
1186                if (Thrift.Protocol.RType[i] === null) {
1187                    continue;
1188                }
1189
1190                ftype = Thrift.Protocol.RType[i];
1191                this.rstack[this.rstack.length - 1] =
1192                    this.rstack[this.rstack.length - 1][i];
1193            }
1194        }
1195
1196        r.fname = '';
1197        r.ftype = ftype;
1198        r.fid = fid;
1199
1200        return r;
1201    },
1202
1203    /** Deserializes the end of a field. */
1204    readFieldEnd: function() {
1205        var pos = this.rpos.pop();
1206
1207        //get back to the right place in the stack
1208        while (this.rstack.length > pos) {
1209            this.rstack.pop();
1210        }
1211
1212    },
1213
1214    /**
1215       @class
1216       @name AnonReadMapBeginReturn
1217       @property {Thrift.Type} ktype - The data type of the key.
1218       @property {Thrift.Type} vtype - The data type of the value.
1219       @property {number} size - The number of elements in the map.
1220     */
1221    /**
1222     * Deserializes the beginning of a map.
1223     * @returns {AnonReadMapBeginReturn}
1224     */
1225    readMapBegin: function() {
1226        var map = this.rstack.pop();
1227        var first = map.shift();
1228        if (first instanceof Array) {
1229          this.rstack.push(map);
1230          map = first;
1231          first = map.shift();
1232        }
1233
1234        var r = {};
1235        r.ktype = Thrift.Protocol.RType[first];
1236        r.vtype = Thrift.Protocol.RType[map.shift()];
1237        r.size = map.shift();
1238
1239        this.rpos.push(this.rstack.length);
1240        this.rstack.push(map.shift());
1241
1242        return r;
1243    },
1244
1245    /** Deserializes the end of a map. */
1246    readMapEnd: function() {
1247        this.readFieldEnd();
1248    },
1249
1250    /**
1251       @class
1252       @name AnonReadColBeginReturn
1253       @property {Thrift.Type} etype - The data type of the element.
1254       @property {number} size - The number of elements in the collection.
1255     */
1256    /**
1257     * Deserializes the beginning of a list.
1258     * @returns {AnonReadColBeginReturn}
1259     */
1260    readListBegin: function() {
1261        var list = this.rstack[this.rstack.length - 1];
1262
1263        var r = {};
1264        r.etype = Thrift.Protocol.RType[list.shift()];
1265        r.size = list.shift();
1266
1267        this.rpos.push(this.rstack.length);
1268        this.rstack.push(list.shift());
1269
1270        return r;
1271    },
1272
1273    /** Deserializes the end of a list. */
1274    readListEnd: function() {
1275        var pos = this.rpos.pop() - 2;
1276        var st = this.rstack;
1277        st.pop();
1278        if (st instanceof Array && st.length > pos && st[pos].length > 0) {
1279          st.push(st[pos].shift());
1280        }
1281    },
1282
1283    /**
1284     * Deserializes the beginning of a set.
1285     * @returns {AnonReadColBeginReturn}
1286     */
1287    readSetBegin: function(elemType, size) {
1288        return this.readListBegin(elemType, size);
1289    },
1290
1291    /** Deserializes the end of a set. */
1292    readSetEnd: function() {
1293        return this.readListEnd();
1294    },
1295
1296    /** Returns an object with a value property set to
1297     *  False unless the next number in the protocol buffer
1298     *  is 1, in which case the value property is True */
1299    readBool: function() {
1300        var r = this.readI32();
1301
1302        if (r !== null && r.value == '1') {
1303            r.value = true;
1304        } else {
1305            r.value = false;
1306        }
1307
1308        return r;
1309    },
1310
1311    /** Returns the an object with a value property set to the
1312        next value found in the protocol buffer */
1313    readByte: function() {
1314        return this.readI32();
1315    },
1316
1317    /** Returns the an object with a value property set to the
1318        next value found in the protocol buffer */
1319    readI16: function() {
1320        return this.readI32();
1321    },
1322
1323    /** Returns the an object with a value property set to the
1324        next value found in the protocol buffer */
1325    readI32: function(f) {
1326        if (f === undefined) {
1327            f = this.rstack[this.rstack.length - 1];
1328        }
1329
1330        var r = {};
1331
1332        if (f instanceof Array) {
1333            if (f.length === 0) {
1334                r.value = undefined;
1335            } else {
1336                if (!f.isReversed) {
1337                    f.reverse();
1338                    f.isReversed = true;
1339                }
1340                r.value = f.pop();
1341            }
1342        } else if (f instanceof Object) {
1343           for (var i in f) {
1344                if (i === null) {
1345                  continue;
1346                }
1347                this.rstack.push(f[i]);
1348                delete f[i];
1349
1350                r.value = i;
1351                break;
1352           }
1353        } else {
1354            r.value = f;
1355            this.rstack.pop();
1356        }
1357
1358        return r;
1359    },
1360
1361    /** Returns the an object with a value property set to the
1362        next value found in the protocol buffer */
1363    readI64: function(f) {
1364        if (f === undefined) {
1365            f = this.rstack[this.rstack.length - 1];
1366        }
1367
1368        var r = {};
1369
1370        if (f instanceof Array) {
1371            if (f.length === 0) {
1372                r.value = undefined;
1373            } else {
1374                if (!f.isReversed) {
1375                    f.reverse();
1376                    f.isReversed = true;
1377                }
1378                r.value = f.pop();
1379            }
1380        } else if (f instanceof Object) {
1381            var int64Object = true;
1382            var objectKeys = Object.keys(f).sort();
1383            var int64Keys = ['buffer', 'offset'];
1384            if (objectKeys.length !== int64Keys.length) {
1385                int64Object = false;
1386            }
1387            for (var it=0; int64Object && it < objectKeys.length; ++it) {
1388                if (objectKeys[it] !== int64Keys[it]) {
1389                    int64Object = false;
1390                }
1391            }
1392            if (int64Object) {
1393                r.value = f;
1394            } else {
1395                for (var i in f) {
1396                    if (i === null) {
1397                    continue;
1398                    }
1399                    this.rstack.push(f[i]);
1400                    delete f[i];
1401
1402                    r.value = i;
1403                    break;
1404                }
1405            }
1406        } else {
1407            r.value = f;
1408            this.rstack.pop();
1409        }
1410        return r;
1411    },
1412
1413    /** Returns the an object with a value property set to the
1414        next value found in the protocol buffer */
1415    readDouble: function() {
1416        return this.readI32();
1417    },
1418
1419    /** Returns the an object with a value property set to the
1420        next value found in the protocol buffer */
1421    readString: function() {
1422        var r = this.readI32();
1423        return r;
1424    },
1425
1426    /** Returns the an object with a value property set to the
1427        next value found in the protocol buffer */
1428    readBinary: function() {
1429        var r = this.readI32();
1430        r.value = atob(r.value);
1431        return r;
1432    },
1433
1434    /**
1435     * Method to arbitrarily skip over data */
1436    skip: function(type) {
1437        var ret, i;
1438        switch (type) {
1439            case Thrift.Type.BOOL:
1440                return this.readBool();
1441
1442            case Thrift.Type.BYTE:
1443                return this.readByte();
1444
1445            case Thrift.Type.I16:
1446                return this.readI16();
1447
1448            case Thrift.Type.I32:
1449                return this.readI32();
1450
1451            case Thrift.Type.I64:
1452                return this.readI64();
1453
1454            case Thrift.Type.DOUBLE:
1455                return this.readDouble();
1456
1457            case Thrift.Type.STRING:
1458                return this.readString();
1459
1460            case Thrift.Type.STRUCT:
1461                this.readStructBegin();
1462                while (true) {
1463                    ret = this.readFieldBegin();
1464                    if (ret.ftype == Thrift.Type.STOP) {
1465                        break;
1466                    }
1467                    this.skip(ret.ftype);
1468                    this.readFieldEnd();
1469                }
1470                this.readStructEnd();
1471                return null;
1472
1473            case Thrift.Type.MAP:
1474                ret = this.readMapBegin();
1475                for (i = 0; i < ret.size; i++) {
1476                    if (i > 0) {
1477                        if (this.rstack.length > this.rpos[this.rpos.length - 1] + 1) {
1478                            this.rstack.pop();
1479                        }
1480                    }
1481                    this.skip(ret.ktype);
1482                    this.skip(ret.vtype);
1483                }
1484                this.readMapEnd();
1485                return null;
1486
1487            case Thrift.Type.SET:
1488                ret = this.readSetBegin();
1489                for (i = 0; i < ret.size; i++) {
1490                    this.skip(ret.etype);
1491                }
1492                this.readSetEnd();
1493                return null;
1494
1495            case Thrift.Type.LIST:
1496                ret = this.readListBegin();
1497                for (i = 0; i < ret.size; i++) {
1498                    this.skip(ret.etype);
1499                }
1500                this.readListEnd();
1501                return null;
1502
1503            default:
1504                throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA);
1505        }
1506    }
1507};
1508
1509
1510/**
1511 * Initializes a MutilplexProtocol Implementation as a Wrapper for Thrift.Protocol
1512 * @constructor
1513 */
1514Thrift.MultiplexProtocol = function(srvName, trans, strictRead, strictWrite) {
1515    Thrift.Protocol.call(this, trans, strictRead, strictWrite);
1516    this.serviceName = srvName;
1517};
1518Thrift.inherits(Thrift.MultiplexProtocol, Thrift.Protocol, 'multiplexProtocol');
1519
1520/** Override writeMessageBegin method of prototype*/
1521Thrift.MultiplexProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
1522
1523    if (type === Thrift.MessageType.CALL || type === Thrift.MessageType.ONEWAY) {
1524        Thrift.Protocol.prototype.writeMessageBegin.call(this, this.serviceName + ':' + name, type, seqid);
1525    } else {
1526        Thrift.Protocol.prototype.writeMessageBegin.call(this, name, type, seqid);
1527    }
1528};
1529
1530Thrift.Multiplexer = function() {
1531    this.seqid = 0;
1532};
1533
1534/** Instantiates a multiplexed client for a specific service
1535 * @constructor
1536 * @param {String} serviceName - The transport to serialize to/from.
1537 * @param {Thrift.ServiceClient} SCl - The Service Client Class
1538 * @param {Thrift.Transport} transport - Thrift.Transport instance which provides remote host:port
1539 * @example
1540 *    var mp = new Thrift.Multiplexer();
1541 *    var transport = new Thrift.Transport("http://localhost:9090/foo.thrift");
1542 *    var protocol = new Thrift.Protocol(transport);
1543 *    var client = mp.createClient('AuthService', AuthServiceClient, transport);
1544*/
1545Thrift.Multiplexer.prototype.createClient = function(serviceName, SCl, transport) {
1546    if (SCl.Client) {
1547        SCl = SCl.Client;
1548    }
1549    var self = this;
1550    SCl.prototype.new_seqid = function() {
1551        self.seqid += 1;
1552        return self.seqid;
1553    };
1554    var client = new SCl(new Thrift.MultiplexProtocol(serviceName, transport));
1555
1556    return client;
1557};
1558
1559
1560
1561var copyList, copyMap;
1562
1563copyList = function(lst, types) {
1564
1565  if (!lst) {return lst; }
1566
1567  var type;
1568
1569  if (types.shift === undefined) {
1570    type = types;
1571  }
1572  else {
1573    type = types[0];
1574  }
1575  var Type = type;
1576
1577  var len = lst.length, result = [], i, val;
1578  for (i = 0; i < len; i++) {
1579    val = lst[i];
1580    if (type === null) {
1581      result.push(val);
1582    }
1583    else if (type === copyMap || type === copyList) {
1584      result.push(type(val, types.slice(1)));
1585    }
1586    else {
1587      result.push(new Type(val));
1588    }
1589  }
1590  return result;
1591};
1592
1593copyMap = function(obj, types) {
1594
1595  if (!obj) {return obj; }
1596
1597  var type;
1598
1599  if (types.shift === undefined) {
1600    type = types;
1601  }
1602  else {
1603    type = types[0];
1604  }
1605  var Type = type;
1606
1607  var result = {}, val;
1608  for (var prop in obj) {
1609    if (obj.hasOwnProperty(prop)) {
1610      val = obj[prop];
1611      if (type === null) {
1612        result[prop] = val;
1613      }
1614      else if (type === copyMap || type === copyList) {
1615        result[prop] = type(val, types.slice(1));
1616      }
1617      else {
1618        result[prop] = new Type(val);
1619      }
1620    }
1621  }
1622  return result;
1623};
1624
1625Thrift.copyMap = copyMap;
1626Thrift.copyList = copyList;
1627