1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2003-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.rpc.xml
13{
14
15import flash.utils.ByteArray;
16import flash.utils.Dictionary;
17import mx.collections.ArrayCollection;
18import mx.collections.IList;
19import mx.messaging.config.LoaderConfig;
20import mx.utils.DescribeTypeCache;
21import mx.utils.object_proxy;
22import mx.utils.ObjectProxy;
23import mx.utils.ObjectUtil;
24
25[ExcludeClass]
26
27/**
28 * Encodes an ActionScript Object graph to XML based on an XML Schema.
29 *
30 * @private
31 */
32public class XMLEncoder extends SchemaProcessor implements IXMLEncoder
33{
34    public function XMLEncoder()
35    {
36        super();
37
38        // Depending on the target player version, xml characters in strings
39        // need or need not be escaped. Prior to version 10, Flex needs to
40        // do it. The version test to determine this behavior is done here, so
41        // that it's not repeated every time xml content is encoded. This is a
42        // fix for bug SDK-18326.
43        if (LoaderConfig.swfVersion >= 10)
44        {
45            _escapeXMLChars = false;
46        }
47    }
48
49    //--------------------------------------------------------------------------
50    //
51    // Methods
52    //
53    //--------------------------------------------------------------------------
54
55    /**
56     * Encodes an ActionScript value as XML.
57     *
58     * @param value The ActionScript value to encode as XML.
59     * @param name The QName of an XML Schema <code>element</code> that
60     * describes how to encode the value, or the name to be used for the
61     * encoded XML node when a type parameter is also specified.
62     * @param type The QName of an XML Schema <code>simpleType</code> or
63     * <code>complexType</code> definition that describes how to encode the
64     * @param definition If neither a top level element nor type exists in the
65     * schema to describe how to encode this value, a custom element definition
66     * can be provided.
67     */
68    public function encode(value:*, name:QName = null, type:QName = null, definition:XML = null):XMLList
69    {
70        var result:XMLList = new XMLList();
71        var content:XML;
72
73        // FIXME: Handle generic name == null case with some default encoding?
74        if (name == null)
75            name = new QName("", "root");
76
77        if (type != null)
78        {
79            content = encodeXSINil(null, name, value);
80            if (content == null)
81            {
82                // If encodeXSINil didn't create content, we do now.
83                content = createElement(name);
84
85                // However, value can still be null if the element wasn't
86                // allowed to be nillable.
87                // FIXME: should we skip null or always create content with xsi:nil?
88                if (value == null)
89                    setValue(content, null);
90                else
91                    encodeType(type, content, name, value);
92            }
93        }
94        else
95        {
96            var elementDefinition:XML = definition;
97            var mustReleaseScope:Boolean = false;
98            if (elementDefinition == null)
99            {
100                elementDefinition = schemaManager.getNamedDefinition(name, constants.elementTypeQName);
101                // If we found a definition through the schemaManager, the relevant
102                // schema was pushed in scope, so we must remember to release it.
103                if (elementDefinition != null)
104                    mustReleaseScope = true;
105            }
106
107            // Encoding is based on an element definition, either a custom one
108            // was provided or we looked one up for the given name. If no definition
109            // was found, encodeElementTopLevel will encode with anyType.
110            content = encodeElementTopLevel(elementDefinition, name, value);
111
112            if (mustReleaseScope)
113                schemaManager.releaseScope();
114        }
115
116        if (content != null)
117            result += content;
118
119        return result;
120    }
121
122
123
124    /**
125     * All content:
126     *     (annotation?, (element | any)*)
127     *
128     * FIXME: This needs work, right now it treats all as a sequence.
129     * @private
130     */
131    public function encodeAll(definition:XML, parent:XMLList, name:QName, value:*, isRequired:Boolean = true):Boolean
132    {
133        return encodeSequence(definition, parent, name, value, isRequired);
134    }
135
136
137
138    /**
139     * Encodes any complex object values as attributes using the XML schema
140     * rules for attribute wildcards.
141     *
142     * FIXME: This needs further investigation of the XML schema spec for
143     * wildcard rules and constraints.
144     *
145     * @private
146     */
147    public function encodeAnyAttribute(definition:XML, parent:XML, name:QName, value:* = undefined, restriction:XML = null):void
148    {
149        // FIXME: honor restrictions for attributes
150
151        if (value !== undefined)
152        {
153            if (!isSimpleValue(value) && !(value is Array))
154            {
155                // FIXME: De we consider namespace filter attributes for
156                // encoding? Also consider skipping reserved attribute names
157                // like xmlns etc? What about non-public namespace properties?
158                for (var propertyName:Object in getProperties(value))
159                {
160                    // Only add wildcard attributes if property has not already been added
161                    if (!hasAttribute(value, propertyName) && !hasValue(value, propertyName))
162                    {
163                        var attributeValue:* = getAttribute(value, propertyName);
164
165                        if (attributeValue != null)
166                            setAttribute(parent, propertyName, attributeValue);
167                    }
168                }
169            }
170        }
171    }
172
173
174
175    /**
176     * Encodes elements based on wildcard rules.
177     *
178     * Any content:
179     *    (annotation?)
180     * @private
181     *
182     */
183    public function encodeAnyElement(definition:XML, siblings:XMLList, name:QName, value:*, isRequired:Boolean=true, encodedVals:Dictionary=null):Boolean
184    {
185        // encodeAnyElement is never called with null value
186//        if (value == null)
187//            return false;
188
189        var maxOccurs:uint = getMaxOccurs(definition);
190        var minOccurs:uint = getMinOccurs(definition);
191
192        if (isSimpleValue(value))
193        {
194        	var item:XML = createElement(name);
195        	setValue(item, value);
196            appendValue(siblings, item);
197        }
198        else if (value is XML || value is XMLList)
199        {
200            // If we have XML or XMLList, just append it to the siblings list.
201            appendValue(siblings, value);
202        }
203        else
204        {
205            // We keep a dictionary of values we have encountered on this stack
206            // in order to detect cyclic references.
207            if (encodedVals == null)
208                encodedVals = new Dictionary(true);
209
210            // Work around AS problem with QName values in Dictionary. Since
211            // QNames cannot possibly create cyclic references, it's OK not to
212            // keep track of them.
213            if (!(value is QName))
214            {
215                if (encodedVals[value] != null)
216                    throw new Error("Cannot encode complex structure. Cyclic references detected.");
217                encodedVals[value] = true;
218            }
219
220            if (value is Array || value is IList)
221            {
222            	// FIXME: Check for maxOccurs and minOccurs.
223                if (value is IList)
224                    value = IList(value).toArray();
225
226                for each (var arrValue:* in value as Array)
227                {
228                    // Since this is an array, we don't need check for nillable
229                    // or isRequired. We must always create a node to preserve
230                    // array indexes.
231					var arrayItem:* = createElement(name);
232
233					if (arrValue == null)
234					{
235					    // Directly set xsi:nil
236        	            setValue(arrayItem, null);
237					}
238					else if (arrValue != null)
239                    {
240                        var arrayChildren:XMLList = new XMLList();
241                        encodeAnyElement(definition, arrayChildren, name, arrValue, isRequired, encodedVals);
242                        if (isSimpleValue(arrValue))
243                        {
244                            // Don't double wrap simple values.
245                            arrayItem = arrayChildren[0];
246                        }
247                        else
248                        {
249                            setValue(arrayItem, arrayChildren);
250                        }
251                    }
252                    appendValue(siblings, arrayItem);
253                }
254            }
255            else
256            {
257                for each (var objProperty:Object in getProperties(value))
258                {
259                    var propValue:* = getValue(value, objProperty);
260                    var propQName:QName = new QName(name.uri, objProperty);
261
262                    // Only encode if property hasn't been encoded yet.
263                    // FIXME: When coming from a group context, if the any element
264                    // is not last in the definition, we will end up encoding subsequent
265                    // named elements twice. We need all sibling names from the definition
266                    // of the group to properly do this.
267                    if (!containsNodeByName(siblings, propQName))
268                    {
269                        var propItem:XML = encodeXSINil(definition, propQName, propValue);
270
271                        if (propItem != null)
272                        {
273                            appendValue(siblings, propItem);
274                        }
275                        else if (propValue != null)
276                        {
277                            var propChildren:XMLList = new XMLList();
278                            encodeAnyElement(definition, propChildren, propQName, propValue, isRequired, encodedVals);
279                            appendValue(siblings, propChildren);
280                        }
281                    }
282                }
283
284            }
285            delete encodedVals[value];
286        }
287
288        // FIXME: figure out isRequired
289        return true;
290    }
291
292
293    /**
294     * An attribute must be based on a simple type and thus will have simple
295     * content encoded as a String.
296     *
297     * This function is used to encode an <code>attribute</code> that may be
298     * named and registered as a top-level <code>schema</code> definition or
299     * in-line from a <code>complexType</code>, <code>extension</code> or
300     * <restriction> of either a <code>complexType</code> or
301     * <code>simpleType</code>, or <code>attributeGroup</code>
302     * definition in any aforementioned parent component.
303     *
304     * If the <code>attribute</code> points to a named definition using a
305     * <code>ref</code> attribute, the reference is resolved to provide the
306     * real definition of the attribute. If the reference cannot be resolved,
307     * an error is thrown.
308     *
309     * If the attribute defines a <code>fixed</code> constraint then any value
310     * provided is ignored and the fixed value is used instead. If a value is
311     * not provided and the attribute defines a <code>default</code>, the
312     * default is used for the encoded attribute. Otherwise if an attribute is
313     * marked as <code>optional</code> and a value is not provided it will be
314     * skipped.
315     *
316     * @param parent The parent instance to which these attributes will be added.
317     * @param definition The XML schema definition of the attribute.
318     * @param value An object with a property name that matches the resolved
319     * attribute name. The property value will be used as the encoded attribute
320     * value.
321     *
322     * FIXME: Attributes are expected to be simple values and must be ultimately
323     * representable as a String. If a complex value is passed to this method
324     * should we assume that we're always looking for a property with the same
325     * name as the attribute? We may need to because if we have a ref then the
326     * name is not known immediately...
327     *
328     * @private
329     */
330    public function encodeAttribute(definition:XML, parent:XML, name:QName, value:* = undefined, restriction:XML = null):void
331    {
332        // <attribute ref="..."> may be used to point to a top-level attribute definition
333        var ref:QName;
334        if (definition.attribute("ref").length() == 1)
335        {
336            ref = schemaManager.getQNameForPrefixedName(definition.@ref, definition, true);
337            definition = schemaManager.getNamedDefinition(ref, constants.attributeQName);
338
339            if (definition == null)
340                throw new Error("Cannot resolve attribute definition for '" + ref + "'");
341        }
342
343        // FIXME: Check restriction for prohibited attribute definitions too
344        var attributeNameString:String = definition.@name.toString();
345        var attributeUse:String = definition.attribute("use").toString();
346        if (attributeUse != "prohibited")
347        {
348            var attributeName:QName = schemaManager.getQNameForAttribute(attributeNameString, getAttributeFromNode("form", definition));
349
350            var attributeFixed:String = getAttributeFromNode("fixed", definition);
351            if (attributeFixed != null)
352            {
353                value = attributeFixed;
354            }
355            else
356            {
357                value = getValue(value, attributeName);
358
359                if (value === undefined)
360                {
361                    var attributeDefault:String = getAttributeFromNode("default", definition);
362                    if (attributeDefault != null)
363                        value = attributeDefault;
364                }
365            }
366
367            var tempElement:*;
368            var attributeFound:Boolean;
369            if (value !== undefined)
370            {
371                var typeDefinition:XML;
372                // we just need a temporary wrapper to pass down as
373                // the parent XML for the encodeSimpleType calls
374                tempElement = <temp/>;
375
376
377
378                // An <attribute> may declare a type="QName" attribute which
379                // refers to either a built-in schema type or a previously
380                // declared <simpleType>
381                var typeName:String = getAttributeFromNode("type", definition);
382                var attributeType:QName;
383                if (typeName != null)
384                    attributeType = schemaManager.getQNameForPrefixedName(definition.@type, definition);
385                else
386                    attributeType = schemaManager.schemaDatatypes.anySimpleTypeQName;
387
388                if (attributeType != null)
389                {
390                    if (isBuiltInType(attributeType))
391                    {
392                        tempElement.appendChild(schemaManager.marshall(value, attributeType, restriction));
393                    }
394                    else
395                    {
396                        // <simpleType>
397                        typeDefinition = schemaManager.getNamedDefinition(attributeType, constants.simpleTypeQName);
398                        if (typeDefinition != null)
399                            encodeSimpleType(typeDefinition, tempElement, attributeName, value, restriction);
400                        else
401                            throw new Error("Cannot find simpleType " + attributeType + " for attribute " + attributeName);
402
403                        // Then release the scope after we've found the attribute type
404                        schemaManager.releaseScope();
405                    }
406                }
407                else
408                {
409                    // Otherwise, an <attribute> may define a single anonymous
410                    // <simpleType> child in-line
411                    typeDefinition = getSingleElementFromNode(definition, constants.simpleTypeQName);
412                    if (typeDefinition != null)
413                    {
414                        encodeSimpleType(typeDefinition, tempElement, attributeName, value, restriction);
415                    }
416                    else if (value != null)
417                    {
418                        // Finally, in the absence of type information we
419                        // just get the attribute value as a String without
420                        // restriction
421                        tempElement.appendChild(value.toString());
422                    }
423                }
424            }
425
426            // FIXME: Should we enforce use="required"?
427            if (tempElement !== undefined)
428            {
429                setAttribute(parent, attributeName, tempElement);
430            }
431        }
432
433        // If we found our attribute by reference, we now release the schema scope
434        if (ref != null)
435            schemaManager.releaseScope();
436    }
437
438    /**
439     * An <code>attributeGroup</code> definition may include a number of
440     * <code>attribute</code> or <code>attributeGroup</code> children, all of
441     * which ultimately combine to form a flat group of attributes for some
442     * type. It may also specify <code>anyAttribute</code> which expands
443     * the definition to accept attributes based on more general criteria
444     * (such excluding or including attributes on namespace).
445     *
446     * This function is used to encode an <code>attributeGroup</code> that may
447     * be named and registered as a top-level <code>schema</code> definition or
448     * in-line from a <code>complexType</code>, <code>extension</code> or
449     * <restriction> of either a <code>complexType</code> or
450     * <code>simpleType</code>, or even another <code>attributeGroup</code>
451     * definition in any aforementioned parent component.
452     *
453     * If the <code>attributeGroup</code> points to a named definition using a
454     * ref attribute, the reference is resolved to provide the real definition
455     * of the attributeGroup. If the reference cannot be resolved, an error is
456     * thrown.
457     *
458     * @param parent The parent instance to which these attributes will be added.
459     * @param definition The XML schema definition of the attributeGroup.
460     * @param value An object with property names that match the resolved
461     * attribute names in the group. The property values will be used as the
462     * encoded attribute values. This argument may be omitted if each attribute
463     * in the group has a fixed or default value.
464     *
465     * @private
466     */
467    public function encodeAttributeGroup(definition:XML, parent:XML, name:QName, value:* = undefined, restriction:XML = null):void
468    {
469        // <attributeGroup ref="..."> may be used to point to a top-level
470        // attributeGroup definition which must first be resolved.
471        var ref:QName;
472        if (definition.attribute("ref").length() == 1)
473        {
474            ref = schemaManager.getQNameForPrefixedName(definition.@ref, definition, true);
475            definition = schemaManager.getNamedDefinition(ref, constants.attributeGroupQName);
476
477            if (definition == null)
478                throw new Error("Cannot resolve attributeGroup definition for '" + ref + "'");
479        }
480
481        // <attribute>
482        var attributes:XMLList = definition.elements(constants.attributeQName);
483        for each (var attributeDefinition:XML in attributes)
484        {
485            encodeAttribute(attributeDefinition, parent, name, value, restriction);
486        }
487
488        // <attributeGroup>
489        var attributeGroups:XMLList = definition.elements(constants.attributeGroupQName);
490        for each (var attributeGroup:XML in attributeGroups)
491        {
492            encodeAttributeGroup(attributeGroup, parent, name, value, restriction);
493        }
494
495        // <anyAttribute>
496        var anyAttribute:XML = getSingleElementFromNode(definition, constants.anyAttributeQName);
497        if (anyAttribute != null)
498        {
499            encodeAnyAttribute(anyAttribute, parent, name, value, restriction);
500        }
501
502        // If we found our attributeGroup by reference, we now release the schema scope
503        if (ref != null)
504            schemaManager.releaseScope();
505    }
506
507    /**
508     * choice:
509     *    (annotation?, (element | group | choice | sequence | any)*)
510     *
511     * @private
512     */
513    public function encodeChoice(definition:XML, parent:XMLList, name:QName, value:*, isRequired:Boolean = true):Boolean
514    {
515        var maxOccurs:uint = getMaxOccurs(definition);
516        var minOccurs:uint = getMinOccurs(definition);
517
518        // If maxOccurs is 0 this choice must not be present.
519        // If minOccurs == 0 the choice is optional so it can be omitted if
520        // a value was not provided.
521        if (maxOccurs == 0)
522            return false;
523        if (value == null && minOccurs == 0)
524            return true;
525
526		var choiceElements:XMLList = definition.elements();
527		var choiceSatisfied:Boolean = true;
528		var lastIndex:uint;
529        var choiceOccurs:uint;
530
531        // We don't enforce occurs bounds on the choice element itself. Since all
532        // child elements of the choice definition would be properties on the
533        // value object, simply looping through the choice children once would
534        // encode the values that apply to each of the child elements.
535
536        // An empty choice is satisfied by default, but if there are choiceElements
537        // we need to start out with choiceSatisfied = false
538        if (choiceElements.length() > 0)
539            choiceSatisfied = false;
540
541		for each (var childDefinition:XML in choiceElements)
542		{
543			if (childDefinition.name() == constants.elementTypeQName)
544			{
545                // <element>
546                choiceSatisfied = encodeGroupElement(childDefinition, parent,
547                    name, value, false) || choiceSatisfied;
548            }
549            else if (childDefinition.name() == constants.sequenceQName)
550            {
551                // <sequence>
552                choiceSatisfied = encodeSequence(childDefinition, parent,
553                    name, value, false) || choiceSatisfied;
554            }
555            else if (childDefinition.name() == constants.groupQName)
556            {
557                // <group>
558                choiceSatisfied = encodeGroupReference(childDefinition, parent,
559                    name, value, false) || choiceSatisfied;
560            }
561            else if (childDefinition.name() == constants.choiceQName)
562            {
563                // <choice>
564                choiceSatisfied = encodeChoice(childDefinition, parent,
565                    name, value, false) || choiceSatisfied;
566            }
567            else if (childDefinition.name() == constants.anyQName)
568            {
569                // <any>
570				choiceSatisfied = encodeAnyElement(childDefinition, parent,
571				    name, value, false) || choiceSatisfied;
572            }
573		}
574
575        return choiceSatisfied;
576    }
577
578
579    /**
580     * Derivation by restriction takes an existing type as the base and creates
581     * a new type by limiting its allowed content to a subset of that allowed
582     * by the base type. Derivation by extension takes an existing type as the
583     * base and creates a new type by adding to its allowed content.
584     *
585     * complexContent:
586     * (annotation?, (restriction | extension))
587     *
588     * @private
589     */
590    public function encodeComplexContent(definition:XML, parent:XML, name:QName, value:*):void
591    {
592        var childDefinition:XML = getSingleElementFromNode(definition, constants.extensionQName, constants.restrictionQName);
593
594        if (childDefinition.name() == constants.extensionQName)
595        {
596            encodeComplexExtension(childDefinition, parent, name, value);
597        }
598        else if (childDefinition.name() == constants.restrictionQName)
599        {
600            encodeComplexRestriction(childDefinition, parent, name, value);
601        }
602    }
603
604
605    /**
606     * complexContent:
607     *   extension:
608     *     (annotation?, ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?), (assert | report)*))
609     *
610     * @private
611     */
612    public function encodeComplexExtension(definition:XML, parent:XML, name:QName, value:*):void
613    {
614        var baseName:String = getAttributeFromNode("base", definition);
615        if (baseName == null)
616            throw new Error ("A complexContent extension must declare a base type.");
617
618        var baseType:QName = schemaManager.getQNameForPrefixedName(baseName, definition);
619
620        // complexContent base type must be a complexType
621        var baseDefinition:XML = schemaManager.getNamedDefinition(baseType, constants.complexTypeQName);
622        if (baseDefinition == null)
623            throw new Error("Cannot find base type definition '" + baseType + "'");
624
625        // FIXME: Should we care if base type is marked final?
626
627        // First encode all of the properties of the base type
628        encodeComplexType(baseDefinition, parent, name, value);
629
630        // Then release the scope of the base type definition
631        schemaManager.releaseScope();
632
633        var childDefinitions:XMLList = definition.elements();
634
635        // Start a separate XMLList for the child elements defined in this extension.
636        // Extension attributes are still encoded directly on the parent.
637        var extChildren:XMLList = new XMLList();
638        for each (var childDefinition:XML in childDefinitions)
639        {
640            if (childDefinition.name() == constants.sequenceQName)
641            {
642                // <sequence>
643                encodeSequence(childDefinition, extChildren, name, value);
644            }
645            else if (childDefinition.name() == constants.groupQName)
646            {
647                // <group>
648                encodeGroupReference(childDefinition, extChildren, name, value);
649            }
650            else if (childDefinition.name() == constants.allQName)
651            {
652                // <all>
653                encodeAll(childDefinition, extChildren, name, value);
654            }
655            else if (childDefinition.name() == constants.choiceQName)
656            {
657                // <choice>
658                encodeChoice(childDefinition, extChildren, name, value);
659            }
660            else if (childDefinition.name() == constants.attributeQName)
661            {
662                // <attribute>
663                encodeAttribute(childDefinition, parent, name, value);
664            }
665            else if (childDefinition.name() == constants.attributeGroupQName)
666            {
667                // <attributeGroup>
668                encodeAttributeGroup(childDefinition, parent, name, value);
669            }
670            else if (childDefinition.name() == constants.anyAttributeQName)
671            {
672                // <anyAttribute>
673                encodeAnyAttribute(childDefinition, parent, name, value);
674            }
675        }
676
677        // We need to add the extension elements to the parent node. However,
678        // we need to handle the case where a value fits both the base and the
679        // extension definitions (strictly speaking that's illegal schema, but
680        // it's used in some cases). We need to keep the values encoded with the
681        // extension definition, so we delete any values with the same names that
682        // we got from encoding the base definition.
683        for each (var extension:XML in extChildren)
684        {
685            // Delete anything already encoded during base type processing, which
686            // matches the full qualified name of this extension element.
687            delete parent[extension.name()];
688            // Also delete unqualified elements with the same local name, since
689            // <any> in the base definition would encode with local names only.
690            delete parent[new QName("", extension.name().localName)];
691            delete parent[new QName(null, extension.name().localName)];
692        }
693        setValue(parent, extChildren);
694    }
695
696    /**
697     * complexContent:
698     *   restriction:
699     *     (annotation?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?), (assert | report)*)
700     *
701     * @private
702     */
703    public function encodeComplexRestriction(restriction:XML, parent:XML, name:QName, value:*):void
704    {
705        var baseName:String = getAttributeFromNode("base", restriction);
706        if (baseName == null)
707            throw new Error ("A complexContent restriction must declare a base type.");
708
709        var baseType:QName = schemaManager.getQNameForPrefixedName(baseName, restriction);
710
711        // FIXME: Validate complex restriction based on the base type definition
712        // complexContent base type must be a complexType
713        // var baseDefinition:XML = schemaManager.getNamedDefinition(baseType, constants.complexTypeQName);
714        // if (baseDefinition == null)
715        //    throw new Error("Cannot find base type definition '" + baseType + "'");
716
717        // FIXME: Should we care if base type is marked final?
718
719        var childDefinitions:XMLList = restriction.elements();
720        var children:XMLList = parent.elements();
721        for each (var childDefinition:XML in childDefinitions)
722        {
723            if (childDefinition.name() == constants.sequenceQName)
724            {
725                // <sequence>
726                encodeSequence(childDefinition, children, name, value);
727            }
728            else if (childDefinition.name() == constants.groupQName)
729            {
730                // <group>
731                encodeGroupReference(childDefinition, children, name, value);
732            }
733            else if (childDefinition.name() == constants.allQName)
734            {
735                // <all>
736                encodeAll(childDefinition, children, name, value);
737            }
738            else if (childDefinition.name() == constants.choiceQName)
739            {
740                // <choice>
741                encodeChoice(childDefinition, children, name, value);
742            }
743            else if (childDefinition.name() == constants.attributeQName)
744            {
745                // <attribute>
746                encodeAttribute(childDefinition, parent, name, value, restriction);
747            }
748            else if (childDefinition.name() == constants.attributeGroupQName)
749            {
750                // <attributeGroup>
751                encodeAttributeGroup(childDefinition, parent, name, value, restriction);
752            }
753            else if (childDefinition.name() == constants.anyAttributeQName)
754            {
755                // <anyAttribute>
756                encodeAnyAttribute(childDefinition, parent, name, value, restriction);
757            }
758        }
759        parent.setChildren(children);
760    }
761
762    public function encodeComplexType(definition:XML, parent:XML, name:QName, value:*, restriction:XML = null):void
763    {
764        var childElements:XMLList = definition.elements();
765
766        var children:XMLList = new XMLList();
767        // FIXME: Investigate if we need to support "base" attribute on
768        // complexType as short-cut as seen in some examples...
769
770        for each (var childDefinition:XML in childElements)
771        {
772            if (childDefinition.name() == constants.sequenceQName)
773            {
774                // <sequence>
775                encodeSequence(childDefinition, children, name, value);
776            }
777            else if (childDefinition.name() == constants.simpleContentQName)
778            {
779                // <simpleContent>
780                encodeSimpleContent(childDefinition, parent, name, value, restriction);
781            }
782            else if (childDefinition.name() == constants.complexContentQName)
783            {
784                // <complexContent>
785                encodeComplexContent(childDefinition, parent, name, value);
786            }
787            else if (childDefinition.name() == constants.groupQName)
788            {
789                // <group>
790                encodeGroupReference(childDefinition, children, name, value);
791            }
792            else if (childDefinition.name() == constants.allQName)
793            {
794                // <all>
795                encodeAll(childDefinition, children, name, value);
796            }
797            else if (childDefinition.name() == constants.choiceQName)
798            {
799                // <choice>
800                encodeChoice(childDefinition, children, name, value);
801            }
802            else if (childDefinition.name() == constants.attributeQName)
803            {
804                // <attribute>
805                encodeAttribute(childDefinition, parent, name, value, restriction);
806            }
807            else if (childDefinition.name() == constants.attributeGroupQName)
808            {
809                // <attributeGroup>
810                encodeAttributeGroup(childDefinition, parent, name, value, restriction);
811            }
812            else if (childDefinition.name() == constants.anyAttributeQName)
813            {
814                // <anyAttribute>
815                encodeAnyAttribute(childDefinition, parent, name, value, restriction);
816            }
817        }
818        setValue(parent, children);
819    }
820
821
822    /**
823     * Used to encode a local element definition (inside a model group).
824     * Handles restrictions on omittability and occurence counts in the
825     * context of the parent model group.
826     * Delegates actual encoding to encodeElementTopLevel once all the
827     * context around the element is known.
828     *
829     * @param definition The XML Schema definition of the local element.
830     * @param parent The XMLList of values encoded in the current level. The
831     * new encoded node should be appended to this XMLList.
832     * @param name The QName to be used for the encoded XML node.
833     * @param value The ActionScript value to encode as XML.
834     * @param isRequired A flag indicating wether the element should meet
835     * its local occurence bounds. For example, the local element may have
836     * minOccurs=1, but be only one of many elements in a choice group, in
837     * which case it is valid not to satisfy the minOccurs requirement.
838     *
839     * @return Wether or not the value provided
840     *
841     * FIXME: Support substitutionGroup, block and redefine?
842     * FIXME: Do we care about abstract or final?
843     * @private
844     */
845    public function encodeGroupElement(definition:XML, siblings:XMLList, name:QName, value:*, isRequired:Boolean = true):Boolean
846    {
847        // <element minOccurs="..." maxOccurs="..."> occur on the local element,
848        // not on a referent, so we capture this information first.
849        var maxOccurs:uint = getMaxOccurs(definition);
850        var minOccurs:uint = getMinOccurs(definition);
851
852        // If the maximum occurence is 0 this element must not be present.
853        if (maxOccurs == 0)
854            return true;
855
856        isRequired = isRequired && minOccurs > 0;
857
858        // <element ref="..."> may be used to point to a top-level element definition
859        var ref:QName;
860        if (definition.attribute("ref").length() == 1)
861        {
862            ref = schemaManager.getQNameForPrefixedName(definition.@ref, definition, true);
863            definition = schemaManager.getNamedDefinition(ref, constants.elementTypeQName);
864            if (definition == null)
865                throw new Error("Cannot resolve element definition for ref '" + ref + "'");
866        }
867
868        var elementName:String = definition.@name.toString();
869        var elementQName:QName = schemaManager.getQNameForElement(elementName, getAttributeFromNode("form", definition));
870
871        // Now that we've resolved the real element name, look for the element's
872        // value on the provided value.
873        var elementValue:* = getValue(value, elementQName);
874        var encodedElement:XML;
875
876        // If minOccurs == 0 the element is optional so we can omit it if
877        // a value was not provided.
878        if (elementValue == null)
879        {
880            encodedElement = encodeElementTopLevel(definition, elementQName, elementValue);
881            if (encodedElement != null)
882                appendValue(siblings, encodedElement);
883
884            // If we found our element by reference, we now release the schema scope
885            if (ref != null)
886                schemaManager.releaseScope();
887
888            // if required, but no value was encoded, the definition is not
889            // satisfied
890            if (isRequired  && encodedElement == null)
891                return false;
892
893            // Otherwise we can return true
894            return true;
895        }
896
897        // We treat maxOccurs="1" as a special case and not check the
898        // occurence because we need to pass through values to SOAP
899        // encoded Arrays which do not rely on minOccurs/maxOccurs
900        if (maxOccurs == 1)
901        {
902            encodedElement = encodeElementTopLevel(definition, elementQName, elementValue);
903            if (encodedElement != null)
904            {
905                appendValue(siblings, encodedElement);
906            }
907            // ...else we just skip the element as a value wasn't provided.
908        }
909        else if (maxOccurs > 1)
910        {
911            var valueOccurence:uint = getValueOccurence(elementValue);
912
913            // If maxOccurs is greater than 1 then we would expect an
914            // Array of values
915            if (valueOccurence < minOccurs)
916            {
917                throw new Error("Value supplied for element '" + elementQName +
918                    "' occurs " + valueOccurence + " times which falls short of minOccurs " +
919                    minOccurs + ".");
920            }
921
922            if (valueOccurence > maxOccurs)
923            {
924                throw new Error("Value supplied for element of type '" + elementQName +
925                    "' occurs " + valueOccurence + " times which exceeds maxOccurs " +
926                    maxOccurs + ".");
927            }
928
929            // Promote non-iterable values to an Array to handle the MXML
930            // single-child property case where the compiler doesn't promote
931            // a property to an Array until two items are present.
932            if (!TypeIterator.isIterable(elementValue))
933                elementValue = [elementValue];
934
935            // Encode element based on occurence within the bounds of
936            // minOccurs and maxOccurs
937            var iter:TypeIterator = new TypeIterator(elementValue);
938
939            for (var i:uint = 0; i < maxOccurs && i < valueOccurence; i++)
940            {
941                var item:*;
942                if (iter.hasNext())
943                {
944                    item = iter.next();
945                }
946                else if (i > minOccurs)
947                {
948                    break;
949                }
950
951                encodedElement = encodeElementTopLevel(definition, elementQName, item);
952                // encodedElement is null if encodeXSINil inside encodeElementTopLevel
953                // was not allowed to create element with xsi:nil for a null or undefined
954                // value. We must still force xsi:nil, because we are encoding an array
955                // and we need to preserve the index.
956                if (encodedElement == null)
957                {
958                    encodedElement = createElement(elementQName);
959                    setValue(encodedElement, null);
960                }
961                appendValue(siblings, encodedElement);
962            }
963        }
964
965        // If we found our element by reference, we now release the schema scope
966        if (ref != null)
967            schemaManager.releaseScope();
968
969        return true;
970    }
971
972    /**
973     * Element content:
974     * (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*))
975     *
976     * @private
977     */
978    public function encodeElementTopLevel(definition:XML, elementQName:QName, value:*):XML
979    {
980        // Let encodeXSINil create an element if null, fixed, or default value
981        // must be used.
982        var element:XML = encodeXSINil(definition, elementQName, value);
983
984        // If ecnodeXSINil created an element, we are done.
985        if (element != null)
986            return element;
987        // If value was null, but element wasn't created, it must be omitted.
988        else if (value == null)
989            return null;
990
991        // Otherwise, just create the element with the given QName. This starts off
992        // a new tree of encoded values with this top level element as the root.
993        element = createElement(elementQName);
994
995        // Check for a simple def first, falling back to complex type handling
996        // and then default handling.
997        var typeAttribute:String = getAttributeFromNode("type", definition);
998        if (typeAttribute != null)
999        {
1000            var typeQName:QName = schemaManager.getQNameForPrefixedName(typeAttribute, definition);
1001            encodeType(typeQName, element, elementQName, value);
1002        }
1003        // Next, check if the element has an in-line <complexType> or
1004        // <simpleType> definition.
1005        else if (definition != null && definition.hasComplexContent())
1006        {
1007            var typeDefinition:XML = getSingleElementFromNode(definition,
1008                                        constants.complexTypeQName,
1009                                        constants.simpleTypeQName);
1010
1011            if (typeDefinition.name() == constants.complexTypeQName)
1012            {
1013                // <complexType>
1014                encodeComplexType(typeDefinition, element, elementQName, value);
1015            }
1016            else if (typeDefinition.name() == constants.simpleTypeQName)
1017            {
1018                // <simpleType>
1019                encodeSimpleType(typeDefinition, element, elementQName, value);
1020            }
1021
1022            // FIXME: Support unique, key, keyref, field, selector
1023        }
1024        else
1025        {
1026            // FIXME: Support <element substitutionGroup="...">
1027            encodeType(constants.anyTypeQName, element, elementQName, value);
1028        }
1029        return element;
1030    }
1031
1032    /**
1033     * The <code>group</code> element allows partial (or complete) content
1034     * models to be reused in complex types. When used inside a choice,
1035     * sequence, complexType, extension or restriction element, it must
1036     * have a ref attribute, specifying the name of a global definition
1037     * of a named model group.
1038     *
1039     * group:
1040     * (annotation?, (all | choice | sequence)?)
1041     *
1042     * @private
1043     */
1044    public function encodeGroupReference(definition:XML, parent:XMLList, name:QName, value:*, isRequired:Boolean = true):Boolean
1045    {
1046        // <group ref="..."> must be used to point to a top-level group definition
1047        var ref:QName;
1048        if (definition.attribute("ref").length() == 1)
1049        {
1050            ref = schemaManager.getQNameForPrefixedName(definition.@ref, definition, true);
1051            definition = schemaManager.getNamedDefinition(ref, constants.groupQName);
1052
1053            if (definition == null)
1054                throw new Error("Cannot resolve group definition for '" + ref + "'");
1055        }
1056        else
1057        {
1058            throw new Error("A group reference element must have the ref attribute");
1059        }
1060
1061        var groupElements:XMLList = definition.elements();
1062        var groupSatisfied:Boolean = false;
1063        for each (var childDefinition:XML in groupElements)
1064        {
1065            if (childDefinition.name() == constants.sequenceQName)
1066            {
1067                // <sequence>
1068                groupSatisfied = encodeSequence(childDefinition, parent, name, value, isRequired);
1069            }
1070            else if (childDefinition.name() == constants.allQName)
1071            {
1072                // <all>
1073                groupSatisfied = encodeAll(childDefinition, parent, name, value, isRequired);
1074            }
1075            else if (childDefinition.name() == constants.choiceQName)
1076            {
1077                // <choice>
1078                groupSatisfied = encodeChoice(childDefinition, parent, name, value, isRequired);
1079            }
1080        }
1081        // We found our group by reference, we now release the schema scope
1082        schemaManager.releaseScope();
1083        return groupSatisfied;
1084    }
1085
1086    /**
1087     * sequence:
1088     *    (annotation?, (element | group | choice | sequence | any)*)
1089     *
1090     * @private
1091     */
1092    public function encodeSequence(definition:XML, siblings:XMLList, name:QName, value:*, isRequired:Boolean=true):Boolean
1093    {
1094        var maxOccurs:uint = getMaxOccurs(definition);
1095        var minOccurs:uint = getMinOccurs(definition);
1096
1097        // If maxOccurs is 0 this sequence must not be present.
1098        // If minOccurs == 0 the sequence is optional so it can be omitted if
1099        // a value was not provided.
1100        if (maxOccurs == 0)
1101            return true;
1102        if (value == null && minOccurs == 0)
1103            return true;
1104
1105		// Note that we can't enforce occurence count on the sequence element
1106		// itself. Since the value is an ActionScript object, any named element
1107		// in the sequence should correspond to a named property on the object.
1108
1109        var sequenceElements:XMLList = definition.elements();
1110        // We loop through the children of the sequence definition. We require
1111        // all child definitions to be satisfied, unless the sequence itself
1112        // doesn't need to be satisfied.
1113        var requireChild:Boolean = isRequired && minOccurs > 0;
1114        var sequenceSatisfied:Boolean = true;
1115
1116		for each (var childDefinition:XML in sequenceElements)
1117		{
1118			sequenceSatisfied = false;
1119			if (childDefinition.name() == constants.elementTypeQName)
1120			{
1121                // <element>
1122                if (!encodeGroupElement(childDefinition, siblings, name, value, isRequired))
1123                	break;
1124            }
1125            else if (childDefinition.name() == constants.groupQName)
1126            {
1127                // <group>
1128                if (!encodeGroupReference(childDefinition, siblings, name, value, isRequired))
1129                	break;
1130            }
1131            else if (childDefinition.name() == constants.choiceQName)
1132            {
1133                // <choice>
1134                if (!encodeChoice(childDefinition, siblings, name, value, isRequired))
1135                	break;
1136            }
1137            else if (childDefinition.name() == constants.sequenceQName)
1138            {
1139                // <sequence>
1140                if (!encodeSequence(childDefinition, siblings, name, value, isRequired))
1141                	break;
1142            }
1143            else if (childDefinition.name() == constants.anyQName)
1144            {
1145                // <any>
1146				if (!encodeAnyElement(childDefinition, siblings, name, value, isRequired))
1147					break;
1148            }
1149            sequenceSatisfied = true;
1150		}
1151
1152		return sequenceSatisfied || !isRequired;
1153    }
1154
1155
1156
1157    /**
1158     * <code>simpleContent</code> specifies that the content will be simple text
1159     * only, that is it conforms to a simple type and will not contain elements,
1160     * although it may also define attributes.
1161     *
1162     * A simpleContent must be defined with an extension or a restriction. An
1163     * extension specifies the attribute definitions that are to be added to the
1164     * type and the base attribute specifies from which simple data type this
1165     * custom type is defined. A restriction for simpleContent is less common,
1166     * although it may be used to prohibit attributes in derived types also
1167     * with simpleContent.
1168     *
1169     * simpleContent
1170     *     (annotation?, (restriction | extension))
1171     *
1172     * @private
1173     */
1174    public function encodeSimpleContent(definition:XML, parent:XML, name:QName, value:*, restriction:XML = null):void
1175    {
1176        var childDefinition:XML = getSingleElementFromNode(definition, constants.extensionQName, constants.restrictionQName);
1177
1178        if (childDefinition != null)
1179        {
1180            var baseName:String = getAttributeFromNode("base", childDefinition);
1181            if (baseName == null)
1182                throw new Error ("A simpleContent extension or restriction must declare a base type.");
1183
1184            var baseType:QName = schemaManager.getQNameForPrefixedName(baseName, childDefinition);
1185
1186            if (!isBuiltInType(baseType))
1187            {
1188                var baseDefinition:XML = schemaManager.getNamedDefinition(baseType,
1189                        constants.complexTypeQName, constants.simpleTypeQName);
1190                if (baseDefinition == null)
1191                    throw new Error("Cannot find base type definition '" + baseType + "'");
1192
1193                // We found our baseType by name so we now release the schema scope
1194                schemaManager.releaseScope();
1195            }
1196
1197            // FIXME: Should we care if base type is marked final?
1198            var simpleValue:*;
1199
1200            // <extension>
1201            if (childDefinition.name() == constants.extensionQName)
1202            {
1203                // simpleContent base type must be a simpleType or a complexType
1204                // that ultimately has simpleContent (FIXME: we currently don't verify the latter)
1205                if (isBuiltInType(baseType))
1206                {
1207                    simpleValue = getSimpleValue(value, name);
1208                    setValue(parent, schemaManager.marshall(simpleValue, baseType, restriction));
1209                }
1210                else
1211                {
1212                    encodeType(baseType, parent, value, restriction);
1213                }
1214
1215                var extensions:XMLList = childDefinition.elements();
1216                for each (var extensionChild:XML in extensions)
1217                {
1218                    if (extensionChild.name() == constants.attributeQName)
1219                    {
1220                        // <attribute>
1221                        encodeAttribute(extensionChild, parent, name, value, restriction);
1222                    }
1223                    else if (extensionChild.name() == constants.attributeGroupQName)
1224                    {
1225                        // <attributeGroup>
1226                        encodeAttributeGroup(extensionChild, parent, name, value, restriction);
1227                    }
1228                    else if (extensionChild.name() == constants.anyAttributeQName)
1229                    {
1230                        // <anyAttribute>
1231                        encodeAnyAttribute(extensionChild, parent, name, value, restriction);
1232                    }
1233                }
1234            }
1235            // <restriction>
1236            else if (childDefinition.name() == constants.restrictionQName)
1237            {
1238                simpleValue = getSimpleValue(value, name);
1239                encodeSimpleRestriction(childDefinition, parent, name, simpleValue);
1240            }
1241        }
1242    }
1243
1244    /**
1245     * A <code>simpleType</code> may declare a list of space separated
1246     * simple content for a single value.
1247     *
1248     * <list
1249     *     id = ID
1250     *     itemType = QName >
1251     *     Content: (annotation?, simpleType?)
1252     * </list>
1253     *
1254     * @private
1255     */
1256    public function encodeSimpleList(definition:XML, parent:XML, name:QName, value:*, restriction:XML = null):void
1257    {
1258        var itemTypeAttribute:String = definition.@itemType;
1259
1260        var itemTypeQName:QName;
1261        var itemDefinition:XML;
1262
1263        // The simple type of each item in the list can be specified by
1264        // either an itemType attribute, or a simpleType inline definition.
1265        if (itemTypeAttribute != "")
1266            itemTypeQName = schemaManager.getQNameForPrefixedName(itemTypeAttribute, definition);
1267        else
1268            itemDefinition = getSingleElementFromNode(definition, constants.simpleTypeQName);
1269
1270        var listValue:String = "";
1271
1272        if (!TypeIterator.isIterable(value))
1273            value = [value];
1274
1275        var iter:TypeIterator = new TypeIterator(value);
1276        while (iter.hasNext())
1277        {
1278            var item:* = iter.next();
1279            var tempElement:* = <temp/>;
1280
1281            // Lists cannot encode null values, since separators are collapsed.
1282            if (item == null)
1283                continue;
1284
1285            if (itemTypeQName != null)
1286                encodeType(itemTypeQName, tempElement, name, item, restriction);
1287            else
1288                encodeSimpleType(itemDefinition, tempElement, name, item, restriction);
1289
1290            listValue = listValue.concat(tempElement.toString());
1291            if (iter.hasNext())
1292                listValue = listValue.concat(" ");
1293        }
1294
1295        setValue(parent, listValue);
1296    }
1297
1298    /**
1299     * simpleType:
1300     *   restriction: (annotation?, (simpleType?,
1301     *       (minExclusive | minInclusive | maxExclusive | maxInclusive |
1302     *       totalDigits | fractionDigits | maxScale | minScale | length |
1303     *       minLength | maxLength | enumeration | whiteSpace | pattern)*))
1304     *
1305     * @private
1306     */
1307    public function encodeSimpleRestriction(restriction:XML, parent:XML, name:QName, value:*):void
1308    {
1309        var simpleTypeDefinition:XML = getSingleElementFromNode(restriction, constants.simpleTypeQName);
1310        if (simpleTypeDefinition != null)
1311        {
1312            encodeSimpleType(simpleTypeDefinition, parent, name, value, restriction);
1313        }
1314        else
1315        {
1316            var baseName:String = getAttributeFromNode("base", restriction);
1317            var baseType:QName = schemaManager.getQNameForPrefixedName(baseName, restriction);
1318
1319            // FIXME: handle anyType
1320            encodeType(baseType, parent, name, value, restriction);
1321        }
1322    }
1323
1324    /**
1325     * <simpleType
1326     *     final = (#all | List of (list | union | restriction | extension))
1327     *     id = ID
1328     *     name = NCName>
1329     *     Content: (annotation?, (restriction | list | union))
1330     * </simpleType>
1331     *
1332     * @private
1333     */
1334    public function encodeSimpleType(definition:XML, parent:XML, name:QName, value:*, restriction:XML = null):void
1335    {
1336        var definitionChild:XML = getSingleElementFromNode(definition,
1337                            constants.restrictionQName,
1338                            constants.listQName,
1339                            constants.unionQName);
1340
1341        if (definitionChild.name() == constants.restrictionQName)
1342        {
1343            // <restriction>
1344            encodeSimpleRestriction(definitionChild, parent, name, value);
1345        }
1346        else if (definitionChild.name() == constants.listQName)
1347        {
1348            // <list>
1349            encodeSimpleList(definitionChild, parent, name, value, restriction);
1350        }
1351        else if (definitionChild.name() == constants.listQName)
1352        {
1353            // <union>
1354            encodeSimpleUnion(definitionChild, parent, name, value, restriction);
1355        }
1356    }
1357
1358    /**
1359     * <union
1360     *     id = ID
1361     *     memberTypes = List of QName >
1362     *     Content: (annotation?, simpleType*)
1363     * </union>
1364     *
1365     * FIXME: This needs a lot of work.
1366     *
1367     * @private
1368     */
1369    public function encodeSimpleUnion(definition:XML, parent:XML, name:QName, value:*, restriction:XML = null):void
1370    {
1371        var memberList:String = getAttributeFromNode("memberTypes", definition);
1372        var memberArray:Array = memberList.split(" ");
1373        var type:QName;
1374        var args:*;
1375
1376        //as the memeber types can contain simple data types like xsd:string or tns:address
1377        for (var i:int = 0; i < memberArray.length; i++)
1378        {
1379            var prefixedName:String = memberArray[i];
1380            var simpleType:QName = schemaManager.getQNameForPrefixedName(prefixedName, definition);
1381
1382            if (!isBuiltInType(simpleType))
1383            {
1384                args = getValue(value, simpleType);
1385                if (args !== undefined)
1386                {
1387                    type = simpleType;
1388                    break;
1389                }
1390            }
1391        }
1392
1393        if (!type)
1394        {
1395            type = schemaManager.schemaDatatypes.stringQName;
1396        }
1397
1398        setValue(parent, schemaManager.marshall(value, type, restriction));
1399    }
1400
1401    /**
1402     * Allow instance specific overrides for concrete type information as
1403     * abstract complexTypes may require a concrete xsi:type definition.
1404     *
1405     * @param parent A reference to the parent XML. Must not be null.
1406     *
1407     * @private
1408     */
1409    public function encodeType(type:QName, parent:XML, name:QName, value:*, restriction:XML = null):void
1410    {
1411        var xsiType:QName = getXSIType(value);
1412        if (xsiType != null)
1413            type = xsiType;
1414
1415        var definition:XML = schemaManager.getNamedDefinition(type,
1416            constants.complexTypeQName, constants.simpleTypeQName);
1417
1418
1419        if (isBuiltInType(type))
1420        {
1421            if (type == constants.anyTypeQName && !isSimpleValue(value))
1422            {
1423                var children:XMLList = new XMLList();
1424                encodeAnyElement(definition, children, name, value);
1425                setValue(parent, children);
1426            }
1427            else
1428            {
1429                setValue(parent, schemaManager.marshall(value, type, restriction));
1430            }
1431
1432            deriveXSIType(parent, type, value);
1433        }
1434        else
1435        {
1436            if (definition == null)
1437                throw new Error("Cannot find definition for type '" + type + "'");
1438
1439            var definitionType:QName = definition.name() as QName;
1440            if (definitionType == constants.complexTypeQName)
1441            {
1442                // <complexType>
1443                encodeComplexType(definition, parent, name, value, restriction);
1444            }
1445            else if (definitionType == constants.simpleTypeQName)
1446            {
1447                // <simpleType>
1448                encodeSimpleType(definition, parent, name, value, restriction);
1449            }
1450            else
1451            {
1452                throw new Error("Invalid type definition " + definitionType);
1453            }
1454        }
1455        // If we found our type definition by name we release the schema scope.
1456        if (definition != null)
1457            schemaManager.releaseScope();
1458    }
1459
1460
1461    /**
1462     * Sets the xsi:nil attribute when necessary
1463     *
1464     * @param definition The Schema definition of the expected type. If
1465     * nillable is strictly enforced, this definition must explicitly
1466     * specify nillable=true.
1467     *
1468     * @param name The name of the element to be created
1469     *
1470     * @param value The value to check
1471     *
1472     * @return content The element where xsi:nil was set, or null if xsi:nil was
1473     * not set.
1474     */
1475    public function encodeXSINil(definition:XML, name:QName, value:*, isRequired:Boolean = true):XML
1476    {
1477        // Check for nillable in the definition only if strictNillability is true.
1478        // Otherwise assume nillable=true.
1479        var nillable:Boolean = true;
1480        if (strictNillability)
1481        {
1482            if (definition != null)
1483                nillable = definition.@nillable.toString() == "true" ? true : false;
1484            else
1485            	nillable = false; //XML schema default for nillable
1486        }
1487
1488        var item:XML;
1489
1490        // <element fixed="...">
1491        // Fixed is forbidden when nillable="true". We enforce that only if
1492        // strictNillability==true. Otherwise we take the fixed value if it
1493        // is provided.
1494        var fixedValue:String = getAttributeFromNode("fixed", definition);
1495        if (!(strictNillability && nillable) && fixedValue != null)
1496        {
1497            item = createElement(name);
1498            setValue(item, schemaManager.marshall(fixedValue, schemaManager.schemaDatatypes.stringQName));
1499            return item;
1500        }
1501
1502        // After we are done with fixed, which can replace even a non-null value,
1503        // we only care about cases where value is null, so we can return otherwise.
1504        if (value != null)
1505            return null;
1506
1507        // <element default="...">
1508        var defaultValue:String = getAttributeFromNode("default", definition);
1509        if (value == null && defaultValue != null)
1510        {
1511            item = createElement(name);
1512            setValue(item, schemaManager.marshall(defaultValue, schemaManager.schemaDatatypes.stringQName));
1513            return item;
1514        }
1515
1516        // If null or undefined, and nillable, we set xsi:nil="true"
1517        // and return the element
1518        if (nillable && value === null && isRequired == true)
1519        {
1520            item = createElement(name);
1521        	setValue(item, null);
1522            return item;
1523        }
1524
1525        return null;
1526    }
1527
1528
1529    /**
1530     * @private
1531     */
1532    public function getAttribute(parent:*, name:*):*
1533    {
1534        return getValue(parent, name);
1535    }
1536
1537    /**
1538     * @private
1539     */
1540    public function hasAttribute(parent:*, name:*):Boolean
1541    {
1542        return (getAttribute(parent, name) !== undefined);
1543    }
1544
1545    /**
1546     * @private
1547     */
1548    public function setAttribute(parent:XML, name:*, value:*):void
1549    {
1550        if (value != null)
1551            parent.@[name] = value.toString();
1552    }
1553
1554    /**
1555     * @private
1556     */
1557    public function getProperties(value:*):Array
1558    {
1559        var classInfo:Object = ObjectUtil.getClassInfo(value as Object, null, {includeReadOnly:false});
1560        return classInfo.properties;
1561    }
1562
1563    /**
1564     * Returns a single XML node with the given name
1565     *
1566     * @private
1567     */
1568    public function createElement(name:*):XML
1569    {
1570        var element:XML;
1571        var elementName:QName;
1572        if (name is QName)
1573            elementName = name as QName;
1574        else
1575            elementName = new QName("", name.toString());
1576
1577        element = <{elementName.localName} />;
1578        if (elementName.uri != null && elementName.uri.length > 0)
1579        {
1580            var prefix:String = schemaManager.getOrCreatePrefix(elementName.uri);
1581            var ns:Namespace = new Namespace(prefix, elementName.uri);
1582            element.setNamespace(ns);
1583        }
1584        return element;
1585    }
1586
1587
1588    /**
1589     * @private
1590     */
1591    public function getSimpleValue(parent:*, name:*):*
1592    {
1593        var simpleValue:* = getValue(parent, name);
1594
1595        // Support legacy _value property for simpleContent
1596        if (simpleValue === undefined)
1597            simpleValue = getValue(parent, "_value");
1598
1599        return simpleValue;
1600    }
1601
1602    /**
1603     * Determines whether a value should be representable as a single, simple
1604     * value, otherwise the object is regarded as "complex" and contains
1605     * child values referenced by index or name.
1606     *
1607     * @private
1608     */
1609    public function isSimpleValue(value:*):Boolean
1610    {
1611        if (value is String || value is Number || value is Boolean
1612            || value is Date || value is int || value is uint
1613            || value is ByteArray)
1614        {
1615            return true;
1616        }
1617
1618        return false;
1619    }
1620
1621    /**
1622     * @private
1623     */
1624    public function getValue(parent:*, name:*):*
1625    {
1626        var value:*;
1627
1628		if (parent is XML || parent is XMLList)
1629        {
1630            var node:XMLList = parent[name];
1631            if (node.length() > 0)
1632                value = node;
1633        }
1634        else if (TypeIterator.isIterable(parent))
1635        {
1636            // We may have an associative Array
1637            if (parent.hasOwnProperty(name) && parent[name] !== undefined)
1638            {
1639                value = resolveNamedProperty(parent, name);
1640            }
1641            else
1642            {
1643                // Otherwise, we just return the value as this may be for an
1644                // ArrayOfSomeType that needs special casing to map directly
1645                // to an Array without a wrapper type
1646                value = parent;
1647            }
1648        }
1649        else if (!isSimpleValue(parent))
1650        {
1651            // We only support the public namespace for now
1652            if (name is QName)
1653                name = QName(name).localName;
1654
1655                value = resolveNamedProperty(parent, name);
1656        }
1657        else
1658        {
1659            // FIXME: Shouldn't this be an error condition?
1660            value = parent;
1661        }
1662
1663        return value;
1664    }
1665
1666
1667    /**
1668     * @private
1669     */
1670    public function hasValue(parent:*, name:*):Boolean
1671    {
1672        return (getValue(parent, name) !== undefined);
1673    }
1674
1675
1676    /**
1677     * @private
1678     */
1679    public function containsNodeByName(list:XMLList, name:QName, strict:Boolean=false):Boolean
1680    {
1681        var currentURI:String = schemaManager.currentSchema.targetNamespace.uri;
1682        for each (var node:XML in list)
1683        {
1684            if (strict || (name.uri != "" && name.uri != null))
1685            {
1686                // If we need strict comparisons, or if name is qualified, and
1687                // not in the default namespace, we match the full QName. However,
1688                // elements already contained in the encoded XMLList could be
1689                // unqualified, so to match them against a qualified name, we use
1690                // the target namespace of the current schema used in encoding,
1691                // which will be the namespace the unqualified elements assume.
1692                if (node.name().uri == "" && currentURI == name.uri)
1693                {
1694                    //compare by localName
1695                    if (node.name().localName == name.localName)
1696                        return true;
1697                }
1698                else if (node.name() == name)
1699                {
1700                    return true;
1701                }
1702            }
1703            else
1704            {
1705                // If we only have a localName, and don't need strict comparisons,
1706                // look for any node with that localName, regardless of namespace.
1707                if (node.name().localName == name.localName)
1708                    return true;
1709            }
1710        }
1711        return false;
1712    }
1713
1714
1715    /**
1716     * Looks up value by name on a complex parent object, considering that the
1717     * name might have to be prepended with an underscore.
1718     * @private
1719     */
1720    public function resolveNamedProperty(parent:*, name:*):*
1721    {
1722        var value:*;
1723        var fallbackName:String = null;
1724
1725        if (!isSimpleValue(parent))
1726        {
1727            try
1728            {
1729                value = parent[name];
1730                // If a value by this name is not defined on a dynamic object,
1731                // try looking up with an underscore.
1732                if (value === undefined)
1733                    fallbackName = "_" + name.toString();
1734            }
1735            catch (e:Error)
1736            {
1737                // If a property with that name doesn't exist on a non-dynamic
1738                // object, an error will be thrown. We should still try the
1739                // fallback name.
1740                fallbackName = "_" + name.toString();
1741
1742            }
1743
1744            if (fallbackName != null && parent.hasOwnProperty(fallbackName))
1745                value = parent[fallbackName];
1746        }
1747
1748        return value;
1749    }
1750
1751
1752
1753    /**
1754     * Assigns value to an XML node.
1755     *
1756     * @param parent The node to assign to. Must be either XML or XMLList.
1757     * If XMLList, it must contain at least one XML element. The value is
1758     * assigned on the last element in the list. If XML, the value is assigned
1759     * directly on parent.
1760     * @param value The value to assign on the parent. If null, the xsi:nil
1761     * attribute is set on the parent. If XML or XMLList, the value is appended
1762     * as child node(s) on the parent. Otherwise the string representation of the
1763     * value is appended as a text node. A value that is explicitly undefined is
1764     * skipped.
1765     *
1766     * @private
1767     */
1768    public function setValue(parent:*, value:*):void
1769    {
1770
1771        if (value !== undefined)
1772        {
1773            var currentChild:XML;
1774            if (parent is XML)
1775                currentChild = parent as XML;
1776            else if (parent is XMLList && parent.length() > 0)
1777                currentChild = parent[parent.length()-1];
1778
1779            if (currentChild != null)
1780            {
1781                if (value === null)
1782                {
1783                	// set xsi:nil attribute if value is specifically null.
1784                    currentChild.@[schemaManager.schemaConstants.nilQName] = "true";
1785                    currentChild.addNamespace(constants.xsiNamespace);
1786                }
1787                else if (value is XML || value is XMLList)
1788                {
1789                    currentChild.appendChild(value);
1790                }
1791                else if (value !== undefined)
1792                {
1793                    // Everything else is treated as simple content, except
1794                    // for undefined, which is skipped.
1795                    currentChild.appendChild(xmlSpecialCharsFilter(Object(value)));
1796                }
1797            }
1798        }
1799    }
1800
1801    /**
1802     * Appends a value (or list of values) directly as
1803     * members of the parent XMLList. Effectively merges
1804     * two XMLLists.
1805     *
1806     * @private
1807     */
1808    public function appendValue(parent:XMLList, value:*):void
1809    {
1810        parent[parent.length()] = value;
1811    }
1812
1813    /**
1814     * Checks to see whether a value defines a custom XSI type to be used
1815     * during encoding, otherwise the default type is returned.
1816     */
1817    protected function getXSIType(value:*):QName
1818    {
1819        var xsiType:QName;
1820
1821        // Allow IXMLSchemaInstance or ObjectProxy to override XSI type
1822        // information, if provided...
1823        if (value != null)
1824        {
1825            if (value is ObjectProxy && value.object_proxy::type != null)
1826            {
1827                xsiType = value.object_proxy::type;
1828            }
1829            else if (value is IXMLSchemaInstance && IXMLSchemaInstance(value).xsiType != null)
1830            {
1831                xsiType = IXMLSchemaInstance(value).xsiType;
1832            }
1833        }
1834
1835        return xsiType;
1836    }
1837
1838    /**
1839     * Record custom XSI type information for this XML node by adding an
1840     * xsi:type attribute with the value set to the qualified type name.
1841     */
1842    protected function setXSIType(parent:XML, type:QName):void
1843    {
1844        var namespaceURI:String = type.uri;
1845        var prefix:String = schemaManager.getOrCreatePrefix(namespaceURI);
1846        var prefixNamespace:Namespace = new Namespace(prefix, namespaceURI);
1847        parent.addNamespace(prefixNamespace);
1848        parent.@[constants.getXSIToken(constants.typeAttrQName)] = prefix + ":" + type.localName;
1849    }
1850
1851    /**
1852     * @private
1853     */
1854    protected function deriveXSIType(parent:XML, type:QName, value:*):void
1855    {
1856    }
1857
1858    /**
1859     * @private
1860     * Default implementation of xmlSpecialCharsFilter. Escapes "&" and "<".
1861     */
1862    private function escapeXML(value:Object):String
1863    {
1864        var str:String = value.toString();
1865        if (_escapeXMLChars)
1866        {
1867            str = str.replace(/&/g, "&amp;").replace(/</g, "&lt;");
1868        }
1869        return str;
1870    }
1871
1872
1873    //--------------------------------------------------------------------------
1874    //
1875    // Properties
1876    //
1877    //--------------------------------------------------------------------------
1878
1879    /**
1880     *
1881     */
1882    public function get strictNillability():Boolean
1883    {
1884        return _strictNillability;
1885    }
1886
1887    /**
1888     *
1889     */
1890    public function set strictNillability(strict:Boolean):void
1891    {
1892        _strictNillability = strict;
1893    }
1894
1895
1896    /**
1897     * Function to be used for escaping XML special characters in simple content.
1898     * Returns default implementation in this class.
1899     */
1900    public function get xmlSpecialCharsFilter():Function
1901    {
1902        return _xmlSpecialCharsFilter;
1903    }
1904
1905    /**
1906     *
1907     */
1908    public function set xmlSpecialCharsFilter(func:Function):void
1909    {
1910        if (func != null)
1911            _xmlSpecialCharsFilter = func;
1912        else
1913            // If setting to null, we revert to built-in default.
1914            _xmlSpecialCharsFilter = escapeXML;
1915    }
1916
1917    //--------------------------------------------------------------------------
1918    //
1919    // Variables
1920    //
1921    //--------------------------------------------------------------------------
1922
1923    private var _strictNillability:Boolean = false;
1924    private var _xmlSpecialCharsFilter:Function = escapeXML;
1925    private var _escapeXMLChars:Boolean = true;
1926
1927}
1928
1929}
1930