]>
git.proxmox.com Git - extjs.git/blob - extjs/packages/amf/src/data/amf/XmlEncoder.js
3 * @class Ext.data.amf.XmlEncoder
4 * This class serializes data in the Action Message Format XML (AMFX) format.
5 * It can write simple and complex objects, to be used in conjunction with an
6 * AMFX-compliant server.
7 * To create an encoded XMl, first construct an Encoder:
9 * var encoder = Ext.create('Ext.data.amf.XmlEncoder');
11 * Then use the writer methods to out data to the :
13 * encoder.writeObject(1);
14 * encoder.writeObject({a: "b"});
16 * And access the data through the #bytes property:
19 * You can also reset the class to start a new body:
23 * Current limitations:
24 * AMF3 format (format:3)
25 * - Each object is written out explicitly, not using the reference tables
26 * supported by the AMFX format. This means the function does NOT support
27 * circular reference objects.
28 * - Objects that aren't Arrays, Dates, Strings, Document (XML) or primitive
29 * values will be written out as anonymous objects with dynamic data.
30 * - If the object has a $flexType field, that field will be used in signifying
31 * the object-type as an attribute, instead of being passed as data.
32 * - There's no JavaScript equivalent to the ByteArray type in ActionScript,
33 * hence data will never be searialized as ByteArrays by the writeObject
34 * function. A writeByteArray method is provided for writing out ByteArray objects.
36 * For more information on working with AMF data please refer to the
37 * [AMF Guide](#/guide/amf).
40 Ext
.define('Ext.data.amf.XmlEncoder', {
42 alias
: 'data.amf.xmlencoder',
45 * @property {String} body - The output string
51 * Utility function to generate a flex-friendly UID
52 * @param {Number} id used in the first 8 chars of the id. If not provided, a random number will be used.
53 * @return {String} a string-encoded opaque UID
55 generateFlexUID: function(id
) {
58 if (id
=== undefined) {
59 id
= Ext
.Number
.randomInt(0, 0xffffffff);
61 // The format of a UID is XXXXXXXX-XXXX-XXXX-XXXX-YYYYYYYYXXXX
62 // where each X is a random hex digit and each Y is a hex digit from the least significant part of a time stamp.
63 t
= (id
+ 0x100000000).toString(16).toUpperCase(); // padded
64 uid
= t
.substr(t
.length
- 8, 8); // last 8 chars
66 for (j
= 0; j
< 3; j
++) {
69 for (i
= 0; i
< 4; i
++) {
70 uid
+= Ext
.Number
.randomInt(0, 15).toString(16).toUpperCase();
75 t
= new Number(new Date()).valueOf().toString(16).toUpperCase(); // get the String representation of milliseconds in hex format
77 if (t
.length
< 8) { // pad with "0" if needed
78 for (i
= 0; i
< t
.length
- 8; i
++) {
84 uid
+= t
.substr(-(8-j
)); // last few chars
85 // and last 4 random digits
86 for (i
= 0; i
< 4; i
++) {
87 uid
+= Ext
.Number
.randomInt(0, 15).toString(16).toUpperCase();
94 * Creates new encoder.
95 * @param {Object} config Configuration options
97 constructor: function(config
) {
98 this.initConfig(config
);
103 * Clears the accumulated data, starting with an empty string
110 * Returns the encoding for undefined (which is the same as the encoding for null)
112 encodeUndefined: function() {
113 return this.encodeNull();
117 * Writes the undefined value to the string
119 writeUndefined: function() {
120 this.write(this.encodeUndefined());
124 * Returns the encoding for null
126 encodeNull: function() {
131 * Writes the null value to the string
133 writeNull: function() {
134 this.write(this.encodeNull());
138 * Returns an encoded boolean
139 * @param {Boolean} val a boolean value
141 encodeBoolean: function(val
) {
152 * Writes a boolean value to the string
153 * @param {Boolean} val a boolean value
155 writeBoolean: function(val
) {
156 this.write(this.encodeBoolean(val
));
161 * Returns an encoded string
162 * @param {String} str the string to encode
164 encodeString: function(str
) {
169 ret
="<string>"+str
+"</string>";
175 * Writes a string tag with the string content.
176 * @param {String} str the string to encode
178 writeString: function(str
) {
179 this.write(this.encodeString(str
));
183 * Returns an encoded int
184 * @param {Number} num the integer to encode
186 encodeInt: function(num
) {
187 return "<int>" + num
.toString() + "</int>";
191 * Writes a int tag with the content.
192 * @param {Number} num the integer to encode
194 writeInt: function(num
) {
195 this.write(this.encodeInt(num
));
199 * Returns an encoded double
200 * @param {Number} num the double to encode
202 encodeDouble: function(num
) {
203 return "<double>" + num
.toString() + "</double>";
207 * Writes a double tag with the content.
208 * @param {Number} num the double to encode
210 writeDouble: function(num
) {
211 this.write(this.encodeDouble(num
));
215 * Returns an encoded number. Decides wheter to use int or double encoding.
216 * @param {Number} num the number to encode
218 encodeNumber: function(num
) {
219 var maxInt
= 0x1fffffff,
220 minSignedInt
= -0xfffffff;
222 if (typeof(num
) !== "number" && !(num
instanceof Number
)) {
223 Ext
.log
.warn("Encoder: writeNumber argument is not numeric. Can't coerce.");
227 // switch to the primitive value for handling:
228 if (num
instanceof Number
) {
231 // Determine if this is an integer or a float.
232 if (num
% 1 === 0 && num
>= minSignedInt
&& num
<= maxInt
) {
233 // The number has no decimal point and is within bounds. Let's encode it.
234 return this.encodeInt(num
);
236 return this.encodeDouble(num
);
241 * Writes a number, deciding if to use int or double as the tag
242 * @param {Number} num the number to encode
244 writeNumber: function(num
) {
245 this.write(this.encodeNumber(num
));
250 * @param {Date} date the date to encode
252 encodeDate: function(date
) {
253 return "<date>" + (new Number(date
)).toString() + "</date>";
257 * Write a date to the string
258 * @param {Date} date the date to encode
260 writeDate: function(date
) {
261 this.write(this.encodeDate(date
));
266 * Encodes one ECMA array element
267 * @param {String} key the name of the element
268 * @param {Object} value the value of the element
269 * @return {String} the encoded key-value pair
271 encodeEcmaElement: function(key
, value
) {
272 var str
= '<item name="' + key
.toString() + '">' + this.encodeObject(value
) + '</item>';
277 * Encodes an array, marking it as an ECMA array if it has associative (non-ordinal) indices
278 * @param {Array} array the array to encode
280 encodeArray: function(array
) {
284 length
= array
.length
, // length is of ordinal section only
287 if (Ext
.isNumeric(i
) && (i
% 1 == 0)) {
288 //this is an integer. Add to ordinals array
289 ordinals
[i
] = this.encodeObject(array
[i
]);
291 ecmaElements
.push(this.encodeEcmaElement(i
, array
[i
]));
294 firstNonOrdinal
=ordinals
.length
;
295 // now, check if we have consecutive numbers in the ordinals array
296 for (i
= 0; i
< ordinals
.length
; i
++) {
297 if (ordinals
[i
] === undefined) {
298 // we have a gap in the array. Mark it - the rest of the items become ECMA elements
303 if (firstNonOrdinal
< ordinals
.length
) {
304 // transfer some of the elements to the ecma array
305 for (i
= firstNonOrdinals
; i
< ordinals
.length
; i
++) {
306 if (ordinals
[i
] !== undefined) {
307 ecmaElements
.push(this.encodeEcmaElement(i
, ordinals
[i
]));
310 ordinals
= ordinals
.slice(0, firstNonOrdinal
);
313 // finally start constructing the string
314 str
= '<array length="' + ordinals
.length
+ '"';
315 if (ecmaElements
.length
> 0) {
316 str
+= ' ecma="true"';
320 // first add the oridnals in consecutive order:
321 for (i
= 0; i
< ordinals
.length
; i
++) { // iterate by counting since we need to guarantee the order
324 // Now add ECMA items
325 for (i
in ecmaElements
) {
326 str
+= ecmaElements
[i
];
328 // And close the array:
334 * Writes an array to the string, marking it as an ECMA array if it has associative (non-ordinal) indices
335 * @param {Array} array the array to encode
337 writeArray: function(array
) {
338 this.write(this.encodeArray(array
));
342 * Encodes an xml document into a CDATA section
343 * @param {XMLElement/HTMLElement} xml an XML document or element (Document type in some browsers)
345 encodeXml: function(xml
) {
346 var str
= this.convertXmlToString(xml
);
347 return "<xml><![CDATA[" + str
+ "]]></xml>";
351 * Write an XML document to the string
352 * @param {XMLElement/HTMLElement} xml an XML document or element (Document type in some browsers)
354 writeXml: function(xml
) {
355 this.write(this.encodeXml(xml
));
359 * Encodes a generic object into AMFX format. If a <tt>$flexType</tt> member is defined, list that as the object type.
360 * @param {Object} obj the object to encode
361 * @return {String} the encoded text
363 encodeGenericObject: function(obj
) {
369 if (i
== "$flexType") {
372 traits
.push(this.encodeString(new String(i
)));
373 values
.push(this.encodeObject(obj
[i
]));
377 str
= '<object type="' +flexType
+ '">';
381 if (traits
.length
> 0) {
383 str
+= traits
.join("");
388 str
+= values
.join("");
394 * Writes a generic object to the string. If a <tt>$flexType</tt> member is defined, list that as the object type.
395 * @param {Object} obj the object to encode
397 writeGenericObject: function(obj
) {
398 this.write(this.encodeGenericObject(obj
));
402 * Encodes a byte arrat in AMFX format
403 * @param {Array} array the byte array to encode
405 encodeByteArray: function(array
) {
407 if (array
.length
> 0) {
409 for (i
= 0; i
< array
.length
; i
++) {
411 if (!Ext
.isNumber(array
[i
])) {
412 Ext
.raise("Byte array contains a non-number: " + array
[i
] + " in index: " + i
);
414 if (array
[i
] < 0 || array
[i
] > 255) {
415 Ext
.raise("Byte array value out of bounds: " + array
[i
]);
418 h
= array
[i
].toString(16).toUpperCase();
419 if (array
[i
] < 0x10) {
424 str
+= "</bytearray>";
426 str
= "<bytearray />";
432 * Writes an AMFX byte array to the string. This is for convenience only and is not called automatically by writeObject.
433 * @param {Array} array the byte array to encode
435 writeByteArray: function(array
) {
436 this.write(this.encodeByteArray(array
));
440 * encode the appropriate data item. Supported types:
447 * - XML Document (identified by being instaneof Document. Can be generated with: new DOMParser()).parseFromString(xml, "text/xml");
451 * @param {Object} item A primitive or object to write to the stream
452 * @return {String} the encoded object in AMFX format
454 encodeObject: function(item
) {
455 var t
= typeof(item
);
456 //Ext.log("Writing " + item + " of type " + t);
457 if (t
=== "undefined") {
458 return this.encodeUndefined();
459 } else if (item
=== null) { // can't check type since typeof(null) returns "object"
460 return this.encodeNull();
461 } else if (Ext
.isBoolean(item
)) {
462 return this.encodeBoolean(item
);
463 } else if (Ext
.isString(item
)) {
464 return this.encodeString(item
);
465 } else if (t
=== "number" || item
instanceof Number
) { // Can't use Ext.isNumeric since it accepts strings as well
466 return this.encodeNumber(item
);
467 } else if (t
=== "object") {
468 // Figure out which object this is
469 if (item
instanceof Date
) {
470 return this.encodeDate(item
);
471 } else if (Ext
.isArray(item
)) {
472 return this.encodeArray(item
);
473 } else if (this.isXmlDocument(item
)) {
474 return this.encodeXml(item
);
476 // Treat this as a generic object with name/value pairs of data.
477 return this.encodeGenericObject(item
);
481 Ext
.log
.warn("AMFX Encoder: Unknown item type " + t
+ " can't be written to stream: " + item
);
484 return null; // if we reached here, return null
488 * Writes the appropriate data item to the string. Supported types:
495 * - XML Document (identified by being instaneof Document. Can be generated with: new DOMParser()).parseFromString(xml, "text/xml");
499 * @param {Object} item A primitive or object to write to the stream
501 writeObject: function(item
) {
502 this.write(this.encodeObject(item
));
506 * Encodes an AMFX remoting message with the AMFX envelope.
507 * @param {Ext.data.amf.RemotingMessage} message the message to pass on to serialize.
509 encodeAmfxRemotingPacket: function(message
) {
511 str
= '<amfx ver="3" xmlns="http://www.macromedia.com/2005/amfx"><body>';
512 str
+= message
.encodeMessage();
513 str
+= '</body></amfx>';
518 * Writes an AMFX remoting message with the AMFX envelope to the string.
519 * @param {Ext.data.amf.RemotingMessage} message the message to pass on to serialize.
521 writeAmfxRemotingPacket: function(params
) {
522 this.write(this.encodeAmfxRemotingPacket(params
));
526 * Converts an XML Document object to a string.
527 * @param {Object} xml XML document to convert (typically Document object)
528 * @return {String} A string representing the document
531 convertXmlToString: function(xml
) {
533 if (window
.XMLSerializer
) {
534 // this is not IE, so:
535 str
= new window
.XMLSerializer().serializeToString(xml
);
537 //no XMLSerializer, might be an old version of IE
544 * Tries to determine if an object is an XML document
545 * @param {Object} item to identify
546 * @return {Boolean} true if it's an XML document, false otherwise
548 isXmlDocument: function(item
) {
549 // We can't test if Document is defined since IE just throws an exception. Instead rely on the DOMParser object
550 if (window
.DOMParser
) {
551 if (Ext
.isDefined(item
.doctype
)) {
555 // Otherwise, check if it has an XML field
556 if (Ext
.isString(item
.xml
)) {
557 // and we can get the xml
564 * Appends a string to the body of the message
565 * @param {String} str the string to append
568 write: function(str
) {