// ------------------------------------------------------------ // XML SCHEMA PARSER // ------------------------------------------------------------ // Reads a schema document, and converts it to a set of // ActionScript object types. Two objects: XMLSchema and // XMLSchemaParser. // ------------------------------------------------------------ // sneville@macromedia.com // gdaniels@macromedia.com // ------------------------------------------------------------ // // SOME ISSUES / TODOS WITH THIS CODE: // // [1] // We always refer to (and also serialize) sub-elements with just a localName, // therefore we don't support: // // // // // ... // // [2] (related to [1]) // We don't deal with name collisions - what happens when either an element/ // attribute name clashes with an ActionScript reserved word, or when we have // both an element and an attribute (or an element in another ns) with the // same name? We should be mangling names when this occurs, and allowing // the use of XPath expressions (i.e. "xpath(obj, '@attr')" or something) to // differentiate. // //////////////////////////////////////////////////////////////////////////// import mx.services.SchemaVersion; import mx.services.SchemaContext; import mx.services.DataType; import mx.services.QName; import mx.services.ElementDecl; import mx.services.SOAPFault; // ------------- // SCHEMA OBJECT // ------------- class mx.services.Schema { static function getSchemas(xmlDocument) { // this convenience method looks only to the root and immediate root child nodes // for schemas, and parses them. Intended to parse arbitrary documents, not schemas // embedded in documents (such as WSDL). Parsers of embedded docs must create their own // SchemaParsers and send the parser the schema element (the WSDL object does this). var schemas = new Array(); // TODO : Implement if needed return schemas; } var targetNamespace; var rootElement; var context:SchemaContext; var schemaVersion:SchemaVersion; var types:Object; var elements:Object; var elementFormDefault; public function Schema(targetNamespace, rootElement, schemaContext) { this.targetNamespace = targetNamespace; this.rootElement = rootElement; this.context = schemaContext; this.schemaVersion = schemaContext.schemaVersion; this.types = new Object(); this.elements = new Object(); } public function registerType(dtype) { if (this.types[dtype.name] == undefined) this.types[dtype.name] = dtype; } public function registerElement(elementObj) { this.elements[elementObj.name] = elementObj; } public function parseType(localPart) { // could be dealing with an element in a type that references an as-yet unparsed type var unparsedTypeNode = this.rootElement.getElementsByReferencedName(localPart, this.schemaVersion.complexTypeQName)[0]; if (unparsedTypeNode != undefined) { return this.parseComplexType(unparsedTypeNode); } unparsedTypeNode = this.rootElement.getElementsByReferencedName(localPart, this.schemaVersion.simpleTypeQName)[0]; if (unparsedTypeNode != undefined) { return this.parseSimpleType(unparsedTypeNode); } } public function parseElement(name) { var unparsedElementNode = this.rootElement.getElementsByReferencedName(name, this.schemaVersion.elementTypeQName)[0]; if (unparsedElementNode != undefined) { var ret = this.parseElementDecl(unparsedElementNode); this.registerElement(ret); return ret; } } public function parseComplexType(typeNode, isAnonymous) { var typeObj = new DataType(); // Default data type for now var typeName = typeNode.attributes["name"]; // We register the type object before parsing the inner content, so // that we catch any circular references while parsing. if (!isAnonymous) { typeObj.name = typeName; typeObj.namespaceURI = this.targetNamespace; typeObj.qname = new QName(typeName, this.targetNamespace); this.registerType(typeObj); } else { typeObj.isAnonymous = true; } var typeChildren = typeNode.childNodes; var numChildren = typeChildren.length; for (var i=0; i-1; i--) { var objPropNode = objProps[i]; var elDecl = this.parseElementDecl(objPropNode); var propDataType = elDecl; // propDataType.schemaType = elDecl.type; // SPECIAL CASE FOR .NET-STYLE "ARRAYS" // When we see a single subelement with maxOccurs "unbounded", we // represent the enclosing element/type as an array. We should have // a switch to turn this off. if ((numProps == 1) && (elDecl.maxOccurs == "unbounded")) { // .NET-style ArrayOfSomething dtype.name = "array" dtype.typeType = DataType.ARRAY_TYPE; dtype.arrayType = propDataType.schemaType; dtype.itemElement = new QName(elDecl.name, elDecl.namespace); } else { if (elDecl.maxOccurs == "unbounded") { var arrayType = new DataType("array", DataType.ARRAY_TYPE); arrayType.arrayType = propDataType.schemaType; propDataType = arrayType; propDataType.itemElement = new QName(elDecl.name, elDecl.namespace); } if (dtype.partTypes == undefined) { dtype.partTypes = new Object(); } dtype.partTypes[elDecl.name] = propDataType; } } return dtype; } function parseSimpleContent(contentNode, typeObj) { var extensionNode = contentNode.firstChild; // Only supports 'extension' right now if (!extensionNode.getQName().equals(this.schemaVersion.extensionQName)) { return; // TODO : Fault } var baseTypeName = extensionNode.attributes.base; var baseQName = extensionNode.getQNameFromString(baseTypeName, true); var baseDataType = this.context.getTypeByQName(baseQName); if (typeObj.partTypes == undefined) typeObj.partTypes = new Object(); // Store the base data type in a special field typeObj.simpleValue = baseDataType; // Get the attribute definitions inside the element var attrNodes = extensionNode.childNodes; for (var i = 0; i < attrNodes.length; i++) { this.parseAttribute(attrNodes[i], typeObj); } } function parseComplexContent(contentNode, typeObj) { var myChildren = contentNode.childNodes; var numChildren = myChildren.length; for (var i=0; i