Initial revision
[OpenSRF.git] / src / javascript / opensrf_domain_object.js
1 // -----------------------------------------------------------------------------
2 // This houses all of the domain object code.
3 // -----------------------------------------------------------------------------
4
5
6
7
8 // -----------------------------------------------------------------------------
9 // DomainObject 
10
11 DomainObject.prototype                                  = new domainObject();
12 DomainObject.prototype.constructor      = DomainObject;
13 DomainObject.prototype.baseClass                = domainObject.prototype.constructor;
14
15 /** Top level DomainObject class.  This most provides convience methods
16   * and a shared superclass
17   */
18 function DomainObject( name ) {
19         if( name ) { this._init_domainObject( name ); }
20 }
21
22 /** Returns the actual element of the given domainObjectAttr. */
23 DomainObject.prototype._findAttr = function ( name ) {
24         
25         var nodes = this.element.childNodes;
26
27         if( ! nodes || nodes.length < 1 ) {
28                 throw new oils_ex_dom( "Invalid xml object in _findAttr: " + this.toString() );
29         }
30
31         var i=0;
32         var node = nodes.item(i);
33
34         while( node != null ) {
35
36                 if( node.nodeName == "oils:domainObjectAttr" &&
37                                 node.getAttribute("name") == name ) {
38                         return node;
39                 }
40
41                 node = nodes.item(++i);
42         }
43
44         return null;
45 }
46
47
48
49
50 /** Returns the value stored in the given attribute */
51 DomainObject.prototype.getAttr = function ( name ) {
52
53         var node = this._findAttr( name );
54         if( node ) { return node.getAttribute( "value" ); }
55         else { 
56                 throw new oils_ex_dom( "getAttr(); Getting nonexistent attribute: " + name ); 
57         }
58 }
59
60 /** Updates the value held by the given attribute */
61 DomainObject.prototype.setAttr = function ( name, value ) {
62
63         var node = this._findAttr( name );
64         if( node ) {
65                 node.setAttribute( "value", value );
66         } else { 
67                 throw new oils_ex_dom( "setAttr(); Setting nonexistent attribute: " + name ); 
68         }
69 }
70
71 /** This takes a raw DOM Element node and creates the DomainObject that the node
72   * embodies and returns the object.  All new DomainObjects should be added to
73   * this list if they require this type of dynamic functionality.
74   * NOTE Much of this will be deprecated as move to a more JSON-centric wire protocol
75   */
76 DomainObject.newFromNode = function( node ) {
77
78         switch( node.getAttribute("name") ) {
79
80                 case "oilsMethod":
81                         return new oilsMethod().replaceNode( node );
82                 
83                 case "oilsMessage":
84                         return new oilsMessage().replaceNode( node );
85
86                 case "oilsResponse":
87                         return new oilsResponse().replaceNode( node );
88
89                 case "oilsResult":
90                         return new oilsResult().replaceNode( node );
91
92                 case "oilsConnectStatus":
93                         return new oilsConnectStatus().replaceNode( node );
94
95                 case "oilsException":
96                         return new oilsException().replaceNode( node );
97
98                 case "oilsMethodException":
99                         return new oilsMethodException().replaceNode( node );
100
101                 case "oilsScalar":
102                         return new oilsScalar().replaceNode( node );
103
104                 case "oilsPair":
105                         return new oilsPair().replaceNode( node );
106
107                 case "oilsArray":
108                         return new oilsArray().replaceNode( node );
109
110                 case "oilsHash":
111                         return new oilsHash().replaceNode( node );
112
113
114         }
115
116 }
117
118
119
120 // -----------------------------------------------------------------------------
121 // oilsMethod
122
123 oilsMethod.prototype = new DomainObject();
124 oilsMethod.prototype.constructor = oilsMethod;
125 oilsMethod.prototype.baseClass = DomainObject.prototype.constructor;
126
127 /**
128   * oilsMethod Constructor
129   * 
130   * @param method_name The name of the method your are sending
131   * to the remote server
132   * @param params_array A Javascript array of the params (any
133   * Javascript data type)
134   */
135
136 function oilsMethod( method_name, params_array ) {
137         this._init_domainObject( "oilsMethod" );
138         this.add( new domainObjectAttr( "method", method_name ) );
139
140         if( params_array ) {
141                 var params = this.doc.createElement( "oils:params" );
142                 this.element.appendChild( params ); 
143                 params.appendChild( this.doc.createTextNode( 
144                                 js2JSON( params_array ) ) ); 
145         }
146 }
147
148
149 /** Locates the params node of this method */
150 oilsMethod.prototype._getParamsNode = function() {
151
152         var nodes = this.element.childNodes;
153         if( ! nodes || nodes.length < 1 ) { return null; }
154         var x=0;
155         var node = nodes.item(x);
156         while( node != null ) {
157                 if( node.nodeName == "oils:params" ) {
158                         return node;
159                 }
160                 node = nodes.item(++x);
161         }
162
163         return null;
164 }
165
166
167 /** Returns an array of param objects  */
168 oilsMethod.prototype.getParams = function() {
169         var node = this._getParamsNode();
170         if(node != null ) { return JSON2js( node->textContent ); }
171 }
172
173
174
175 // -----------------------------------------------------------------------------
176 // oilsMessage
177
178 // -----------------------------------------------------------------------------
179 // oilsMessage message types
180 // -----------------------------------------------------------------------------
181 /** CONNECT Message type */
182 oilsMessage.CONNECT             = 'CONNECT';
183 /** DISCONNECT Message type */
184 oilsMessage.DISCONNECT  = 'DISCONNECT';
185 /** STATUS Message type */
186 oilsMessage.STATUS              = 'STATUS';
187 /** REQUEST Message type */
188 oilsMessage.REQUEST             = 'REQUEST';
189 /** RESULT Message type */
190 oilsMessage.RESULT              = 'RESULT';
191
192
193 oilsMessage.prototype = new DomainObject();
194 oilsMessage.prototype.constructor = oilsMessage;
195 oilsMessage.prototype.baseClass = DomainObject.prototype.constructor;
196
197 /** Core XML object for message passing */
198 function oilsMessage( type, protocol, user_auth ) {
199
200         if( !( type && protocol) ) { 
201                 type = oilsMessage.CONNECT; 
202                 protocol = "1";
203         }
204
205         if( ! ( type == oilsMessage.CONNECT             ||
206                                 type == oilsMessage.DISCONNECT  ||
207                                 type == oilsMessage.STATUS                      ||
208                                 type == oilsMessage.REQUEST             ||
209                                 type == oilsMessage.RESULT      ) ) {
210                 throw new oils_ex_message( "Attempt to create oilsMessage with incorrect type: " + type );
211         }
212
213         this._init_domainObject( "oilsMessage" );
214
215         this.add( new domainObjectAttr( "type", type ) );
216         this.add( new domainObjectAttr( "threadTrace", 0 ) );
217         this.add( new domainObjectAttr( "protocol", protocol ) );       
218
219         if( user_auth ) { this.add( user_auth ); }
220
221 }
222
223 /** Builds a new oilsMessage from raw xml.
224   * Checks are taken to make sure the xml is supposed to be an oilsMessage.  
225   * If not, an oils_ex_dom exception is throw.
226   */
227 oilsMessage.newFromXML = function( xml ) {
228
229         try {
230
231                 if( ! xml || xml == "" ) { throw 1; }
232
233                 var doc = new DOMParser().parseFromString( xml, "text/xml" );
234                 if( ! doc ) { throw 1; }
235
236                 var root = doc.documentElement;
237                 if( ! root ) { throw 1; }
238
239                 // -----------------------------------------------------------------------------
240                 // There are two options here.  One is that we were provided the full message
241                 // xml (i.e. contains the <oils:root> tag.  The other option is that we were
242                 // provided just the message xml portion (top level element is the 
243                 // <oils:domainObject>
244                 // -----------------------------------------------------------------------------
245                 
246                 var element;
247                 if( root.nodeName == "oils:root"  ) { 
248
249                         element = root.firstChild;
250                         if( ! element ) { throw 1; }
251
252                 } else { 
253
254                         if( root.nodeName == "oils:domainObject" ) { 
255                                 element = root;
256
257                         } else { throw 1; }
258
259                 }
260
261
262                 if( element.nodeName != "oils:domainObject" ) { throw 1; } 
263                 if( element.getAttribute( "name" ) != "oilsMessage" ) { throw 1; }
264
265                 return new oilsMessage().replaceNode( element );
266
267         } catch( E ) {
268
269                 if( E && E.message ) {
270                         throw new oils_ex_dom( "Bogus XML for creating oilsMessage: " + E.message + "\n" + xml );
271
272                 } else {
273                         throw new oils_ex_dom( "Bogus XML for creating oilsMessage:\n" + xml );
274                 }
275         }
276
277         return msg;
278 }
279
280
281
282 /** Adds a copy of the given DomainObject to the message */
283 oilsMessage.prototype.addPayload = function( new_DomainObject ) {
284         this.add( new_DomainObject );
285 }
286
287
288 /** Returns the top level DomainObject contained in this message
289   *
290   * Note:  The object retuturned will be the actual object, not just a 
291   * generic DomainObject - e.g. oilsException.
292   */
293 oilsMessage.prototype.getPayload = function() {
294         
295         var nodes = this.element.childNodes;
296         var x=0;
297         var node = nodes.item(0);
298
299         while( node != null ) {
300
301                 if( node.nodeName == "oils:domainObject" ) {
302
303                         new Logger().debug( "Building oilsMessage payload from\n" + 
304                                 new XMLSerializer().serializeToString( node ), Logger.DEBUG );
305                         return DomainObject.newFromNode( node );
306                 }
307                 node = nodes.item(++x);
308         }
309
310         return null;
311 }
312
313 oilsMessage.prototype.getUserAuth = function() {
314
315         var nodes = this.element.getElementsByTagName( "oils:userAuth" );
316         if( ! nodes ) { return null; }
317
318         var auth_elem = nodes.item(0); // should only be one
319         if ( ! auth_elem ) { return null; }
320
321         return new userAuth().replaceNode( auth_elem );
322
323 }
324
325 /** Returns the type of the message */
326 oilsMessage.prototype.getType = function() {
327         return this.getAttr( "type" );
328 }
329
330 /** Returns the message protocol */
331 oilsMessage.prototype.getProtocol = function() {
332         return this.getAttr( "protocol" );
333 }
334
335 /** Returns the threadTrace */
336 oilsMessage.prototype.getThreadTrace = function() {
337         return this.getAttr( "threadTrace" );
338 }
339
340 /** Sets the thread trace - MUST be an integer */
341 oilsMessage.prototype.setThreadTrace = function( trace ) {
342         this.setAttr( "threadTrace", trace );
343 }
344
345 /** Increments the thread trace by 1 */
346 oilsMessage.prototype.updateThreadTrace = function() {
347         var tt = this.getThreadTrace();
348         return this.setThreadTrace( ++tt );
349 }
350
351
352
353
354
355 // -----------------------------------------------------------------------------
356 // oilsResponse
357
358
359 // -----------------------------------------------------------------------------
360 // Response codes
361 // -----------------------------------------------------------------------------
362
363 /**
364   * Below are the various response statuses
365   */
366 oilsResponse.STATUS_CONTINUE                                            = 100
367
368 oilsResponse.STATUS_OK                                                          = 200
369 oilsResponse.STATUS_ACCEPTED                                            = 202
370 oilsResponse.STATUS_COMPLETE                                            = 205
371
372 oilsResponse.STATUS_REDIRECTED                                  = 307
373
374 oilsResponse.STATUS_BADREQUEST                                  = 400
375 oilsResponse.STATUS_UNAUTHORIZED                                        = 401
376 oilsResponse.STATUS_FORBIDDEN                                           = 403
377 oilsResponse.STATUS_NOTFOUND                                            = 404
378 oilsResponse.STATUS_NOTALLOWED                                  = 405
379 oilsResponse.STATUS_TIMEOUT                                             = 408
380 oilsResponse.STATUS_EXPFAILED                                           = 417
381 oilsResponse.STATUS_INTERNALSERVERERROR         = 500
382 oilsResponse.STATUS_NOTIMPLEMENTED                              = 501
383 oilsResponse.STATUS_VERSIONNOTSUPPORTED         = 505
384
385
386 oilsResponse.prototype = new DomainObject();
387 oilsResponse.prototype.constructor = oilsResponse;
388 oilsResponse.prototype.baseClass = DomainObject.prototype.constructor;
389
390 /** Constructor.  status is the text describing the message status and
391   * statusCode must correspond to one of the oilsResponse status code numbers
392   */
393 function oilsResponse( name, status, statusCode ) {
394         if( name && status && statusCode ) {
395                 this._initResponse( name, status, statusCode );
396         }
397 }
398
399 /** Initializes the reponse XML */
400 oilsResponse.prototype._initResponse = function( name, status, statusCode ) {
401
402         this._init_domainObject( name );
403         this.add( new domainObjectAttr( "status", status ));
404         this.add( new domainObjectAttr( "statusCode", statusCode ) );
405 }
406
407
408
409 /** Returns the status of the response */
410 oilsResponse.prototype.getStatus = function() {
411         return this.getAttr( "status" );
412 }
413
414 /** Returns the statusCode of the response */
415 oilsResponse.prototype.getStatusCode = function() {
416         return this.getAttr("statusCode");
417 }
418
419
420 oilsConnectStatus.prototype = new oilsResponse();
421 oilsConnectStatus.prototype.constructor = oilsConnectStatus;
422 oilsConnectStatus.prototype.baseClass = oilsResponse.prototype.constructor;
423
424 /** Constructor.  These give us info on our connection attempts **/
425 function oilsConnectStatus( status, statusCode ) {
426         this._initResponse( "oilsConnectStatus", status, statusCode );
427 }
428
429
430 oilsResult.prototype = new oilsResponse();
431 oilsResult.prototype.constructor = oilsResult;
432 oilsResult.prototype.baseClass = oilsResponse.prototype.constructor;
433
434
435 /** Constructor.  These usually carry REQUEST responses */
436 function oilsResult( status, statusCode ) {
437         if( status && statusCode ) {
438                 this._initResponse( "oilsResult", status, statusCode );
439         }
440 }
441
442 /** Returns the top level DomainObject within this result message.
443   * Note:  The object retuturned will be the actual object, not just a 
444   * generic DomainObject - e.g. oilsException.
445   */
446 oilsResult.prototype.getContent = function() {
447
448         var nodes = this.element.childNodes;
449         if( ! nodes || nodes.length < 1 ) {
450                 throw new oils_ex_dom("Content node for oilsResult is invalid\n" + this.toString() );
451         }
452         var x = 0;
453         var node = nodes.item(x);
454         while( node != null ) {
455
456                 if( node.nodeName == "oils:domainObject"                                        ||
457                                 node.nodeName == "oils:domainObjectCollection" ) {
458
459                         return DomainObject.newFromNode( node );
460                 }
461                 node = nodes.item(++x);
462         }
463
464         throw new oils_ex_dom("Content node for oilsResult is invalid\n" + this.toString() );
465 }
466
467
468 oilsException.prototype = new oilsResponse();
469 oilsException.prototype.constructor = oilsException;
470 oilsException.prototype.baseClass = oilsResponse.prototype.constructor;
471
472 /** Top level exception */
473 function oilsException( status, statusCode ) {
474         if( status && statusCode ) {
475                 this._initResponse( "oilsException", status, statusCode );
476         }
477 }
478
479
480 oilsMethodException.prototype = new oilsException();
481 oilsMethodException.prototype.constructor = oilsMethodException;
482 oilsMethodException.prototype.baseClass = oilsException.prototype.constructor;
483
484 /** Method exception */
485 function oilsMethodException( status, statusCode ) {
486         if( status && statusCode ) {
487                 this._initResponse( "oilsMethodException", status, statusCode );
488         }
489 }
490
491
492 // -----------------------------------------------------------------------------
493 // oilsScalar
494
495
496 oilsScalar.prototype = new DomainObject();
497 oilsScalar.prototype.constructor = oilsScalar;
498 oilsScalar.prototype.baseClass = DomainObject.prototype.constructor;
499
500 /** Contains a single JSON item as its text content */
501 function oilsScalar( value ) {
502         this._init_domainObject( "oilsScalar" );
503         this.element.appendChild( this.doc.createTextNode( value ) );
504 }
505
506 /** Returns the contained item as a Javascript object */
507 oilsScalar.prototype.getValue = function() {
508         return JSON2js( this.element.textContent );
509 }
510
511
512 // -----------------------------------------------------------------------------
513 // oilsPair
514
515
516 oilsPair.prototype = new DomainObject()
517 oilsPair.prototype.constructor = oilsPair;
518 oilsPair.prototype.baseClass = DomainObject.prototype.constructor;
519
520 function oilsPair( key, value ) {
521
522         this._init_domainObject( "oilsPair" );
523         this.element.appendChild( this.doc.createTextNode( value ) );
524         this.element.setAttribute( "key", key );
525 }
526
527 oilsPair.prototype.getKey = function() {
528         return this.element.getAttribute( "key" );
529 }
530
531 oilsPair.prototype.getValue = function() {
532         return this.element.textContent;
533 }
534
535
536
537 // -----------------------------------------------------------------------------
538 // oilsArray - is a domainObjectCollection that stores a list of domainObject's
539 // or domainObjectCollections.
540 // -----------------------------------------------------------------------------
541
542 oilsArray.prototype = new domainObjectCollection()
543 oilsArray.prototype.constructor = oilsArray;
544 oilsArray.prototype.baseClass = domainObjectCollection.prototype.constructor;
545
546 function oilsArray( obj_list ) {
547         this._initCollection( "oilsArray" );
548         if(obj_list) { this.initArray( obj_list ); }
549 }
550
551 // -----------------------------------------------------------------------------
552 // Adds the array of objects to the array, these should be actual objects, not
553 // DOM nodes/elements, etc.
554 // -----------------------------------------------------------------------------
555 oilsArray.prototype.initArray = function( obj_list ) {
556         if( obj_array != null && obj_array.length > 0 ) {
557                 for( var i= 0; i!= obj_array.length; i++ ) {
558                         this.add( obj_array[i] );
559                 }
560         }
561 }
562
563 // -----------------------------------------------------------------------------
564 // Returns an array of DomainObjects or domainObjectCollections.  The objects
565 // returned will already be 'cast' into the correct object. i.e. you will not
566 // receieve an array of DomainObjects, but instead an array of oilsScalar's or
567 // an array of oilsHashe's, etc.
568 // -----------------------------------------------------------------------------
569 oilsArray.prototype.getObjects = function() {
570
571         var obj_list = new Array();
572         var nodes = this.element.childNodes;
573         var i=0;
574         var node = nodes.item(i);
575
576         while( node != null ) {
577
578                 if( node.nodeName == "oils:domainObject"                                        ||
579                                 node.nodeName == "oils:domainObjectCollection" ) {
580                         obj_list[i++] = DomainObject.newFromNode( node );
581                 }
582                 node = nodes.item(i);
583         }
584
585         return obj_list;
586 }
587
588
589 // -----------------------------------------------------------------------------
590 // oilsHash - an oilsHash is an oilsArray except it only stores oilsPair's
591 // -----------------------------------------------------------------------------
592
593 oilsHash.prototype = new oilsArray();
594 oilsHash.prototype.constructor = oilsHash;
595 oilsHash.prototype.baseClass = oilsArray.prototype.constructor;
596
597 function oilsHash( pair_array ) {
598         this._initCollection("oilsHash");
599         if(pair_array) { this.initArray( pair_array ); }
600 }
601
602 // -----------------------------------------------------------------------------
603 // Returns the array of oilsPairs objects that this hash contains
604 // -----------------------------------------------------------------------------
605 oilsHash.prototype.getPairs = function() {
606         return this.getObjects();
607 }
608
609