"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
exports.__esModule = true;
var assert_1 = require("assert");
var debugBuilder = require("debug");
var _ = require("lodash");
var utils_1 = require("../utils");
var debug = debugBuilder('node-soap');
var Primitives = {
    string: 1,
    boolean: 1,
    decimal: 1,
    float: 1,
    double: 1,
    anyType: 1,
    byte: 1,
    int: 1,
    long: 1,
    short: 1,
    negativeInteger: 1,
    nonNegativeInteger: 1,
    positiveInteger: 1,
    nonPositiveInteger: 1,
    unsignedByte: 1,
    unsignedInt: 1,
    unsignedLong: 1,
    unsignedShort: 1,
    duration: 0,
    dateTime: 0,
    time: 0,
    date: 0,
    gYearMonth: 0,
    gYear: 0,
    gMonthDay: 0,
    gDay: 0,
    gMonth: 0,
    hexBinary: 0,
    base64Binary: 0,
    anyURI: 0,
    QName: 0,
    NOTATION: 0
};
var Element = /** @class */ (function () {
    function Element(nsName, attrs, options, schemaAttrs) {
        this.allowedChildren = {};
        this.children = [];
        var parts = utils_1.splitQName(nsName);
        this.nsName = nsName;
        this.prefix = parts.prefix;
        this.name = parts.name;
        this.children = [];
        this.xmlns = {};
        this.schemaXmlns = {};
        this._initializeOptions(options);
        for (var key in attrs) {
            var match = /^xmlns:?(.*)$/.exec(key);
            if (match) {
                this.xmlns[match[1] ? match[1] : utils_1.TNS_PREFIX] = attrs[key];
            }
            else {
                if (key === 'value') {
                    this[this.valueKey] = attrs[key];
                }
                else {
                    this['$' + key] = attrs[key];
                }
            }
        }
        for (var schemaKey in schemaAttrs) {
            var schemaMatch = /^xmlns:?(.*)$/.exec(schemaKey);
            if (schemaMatch && schemaMatch[1]) {
                this.schemaXmlns[schemaMatch[1]] = schemaAttrs[schemaKey];
            }
        }
        if (this.$targetNamespace !== undefined) {
            // Add targetNamespace to the mapping
            this.xmlns[utils_1.TNS_PREFIX] = this.$targetNamespace;
        }
        this.init();
    }
    Element.prototype.deleteFixedAttrs = function () {
        this.children && this.children.length === 0 && delete this.children;
        this.xmlns && Object.keys(this.xmlns).length === 0 && delete this.xmlns;
        delete this.nsName;
        delete this.prefix;
        delete this.name;
    };
    Element.prototype.startElement = function (stack, nsName, attrs, options, schemaXmlns) {
        if (!this.allowedChildren) {
            return;
        }
        var ChildClass = this.allowedChildren[utils_1.splitQName(nsName).name];
        if (ChildClass) {
            var child = new ChildClass(nsName, attrs, options, schemaXmlns);
            child.init();
            stack.push(child);
        }
        else {
            this.unexpected(nsName);
        }
    };
    Element.prototype.endElement = function (stack, nsName) {
        if (this.nsName === nsName) {
            if (stack.length < 2) {
                return;
            }
            var parent_1 = stack[stack.length - 2];
            if (this !== stack[0]) {
                _.defaultsDeep(stack[0].xmlns, this.xmlns);
                // delete this.xmlns;
                parent_1.children.push(this);
                parent_1.addChild(this);
            }
            stack.pop();
        }
    };
    Element.prototype.addChild = function (child) {
        return;
    };
    Element.prototype.unexpected = function (name) {
        throw new Error('Found unexpected element (' + name + ') inside ' + this.nsName);
    };
    Element.prototype.description = function (definitions, xmlns) {
        return this.$name || this.name;
    };
    Element.prototype.init = function () {
    };
    Element.prototype._initializeOptions = function (options) {
        if (options) {
            this.valueKey = options.valueKey || '$value';
            this.xmlKey = options.xmlKey || '$xml';
            this.ignoredNamespaces = options.ignoredNamespaces || [];
        }
        else {
            this.valueKey = '$value';
            this.xmlKey = '$xml';
            this.ignoredNamespaces = [];
        }
    };
    return Element;
}());
exports.Element = Element;
var ElementElement = /** @class */ (function (_super) {
    __extends(ElementElement, _super);
    function ElementElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'annotation',
            'complexType',
            'simpleType',
        ]);
        return _this;
    }
    ElementElement.prototype.description = function (definitions, xmlns) {
        var element = {};
        var name = this.$name;
        var isMany = !this.$maxOccurs ? false : (typeof this.$maxOccurs === 'string' ? (this.$maxOccurs === 'unbounded') : (this.$maxOccurs > 1));
        if (this.$minOccurs !== this.$maxOccurs && isMany) {
            name += '[]';
        }
        if (xmlns && xmlns[utils_1.TNS_PREFIX]) {
            this.$targetNamespace = xmlns[utils_1.TNS_PREFIX];
        }
        var type = this.$type || this.$ref;
        if (type) {
            type = utils_1.splitQName(type);
            var typeName = type.name;
            var ns = xmlns && xmlns[type.prefix] ||
                ((definitions.xmlns[type.prefix] !== undefined || definitions.xmlns[this.targetNSAlias] !== undefined) && this.schemaXmlns[type.prefix]) ||
                definitions.xmlns[type.prefix];
            var schema = definitions.schemas[ns];
            var typeElement = schema && (this.$type ? schema.complexTypes[typeName] || schema.types[typeName] : schema.elements[typeName]);
            if (ns && definitions.schemas[ns]) {
                xmlns = definitions.schemas[ns].xmlns;
            }
            if (typeElement && !(typeName in Primitives)) {
                if (!(typeName in definitions.descriptions.types)) {
                    var elem_1 = {};
                    definitions.descriptions.types[typeName] = elem_1;
                    var description_1 = typeElement.description(definitions, xmlns);
                    if (typeof description_1 === 'string') {
                        elem_1 = description_1;
                    }
                    else {
                        Object.keys(description_1).forEach(function (key) {
                            elem_1[key] = description_1[key];
                        });
                    }
                    if (this.$ref) {
                        element = elem_1;
                    }
                    else {
                        element[name] = elem_1;
                    }
                    if (typeof elem_1 === 'object') {
                        elem_1.targetNSAlias = type.prefix;
                        elem_1.targetNamespace = ns;
                    }
                    definitions.descriptions.types[typeName] = elem_1;
                }
                else {
                    if (this.$ref) {
                        element = definitions.descriptions.types[typeName];
                    }
                    else {
                        element[name] = definitions.descriptions.types[typeName];
                    }
                }
            }
            else {
                element[name] = this.$type;
            }
        }
        else {
            var children = this.children;
            element[name] = {};
            for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
                var child = children_1[_i];
                if (child instanceof ComplexTypeElement || child instanceof SimpleTypeElement) {
                    element[name] = child.description(definitions, xmlns);
                }
            }
        }
        return element;
    };
    return ElementElement;
}(Element));
exports.ElementElement = ElementElement;
var AnyElement = /** @class */ (function (_super) {
    __extends(AnyElement, _super);
    function AnyElement() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return AnyElement;
}(Element));
exports.AnyElement = AnyElement;
var InputElement = /** @class */ (function (_super) {
    __extends(InputElement, _super);
    function InputElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'body',
            'documentation',
            'header',
            'SecuritySpecRef',
        ]);
        return _this;
    }
    InputElement.prototype.addChild = function (child) {
        if (child instanceof BodyElement) {
            this.use = child.$use;
            if (this.use === 'encoded') {
                this.encodingStyle = child.$encodingStyle;
            }
            this.children.pop();
        }
    };
    return InputElement;
}(Element));
exports.InputElement = InputElement;
var OutputElement = /** @class */ (function (_super) {
    __extends(OutputElement, _super);
    function OutputElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'body',
            'documentation',
            'header',
            'SecuritySpecRef',
        ]);
        return _this;
    }
    OutputElement.prototype.addChild = function (child) {
        if (child instanceof BodyElement) {
            this.use = child.$use;
            if (this.use === 'encoded') {
                this.encodingStyle = child.$encodingStyle;
            }
            this.children.pop();
        }
    };
    return OutputElement;
}(Element));
exports.OutputElement = OutputElement;
var SimpleTypeElement = /** @class */ (function (_super) {
    __extends(SimpleTypeElement, _super);
    function SimpleTypeElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'restriction',
        ]);
        return _this;
    }
    SimpleTypeElement.prototype.description = function (definitions) {
        for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
            var child = _a[_i];
            if (child instanceof RestrictionElement) {
                return [this.$name, child.description()].filter(Boolean).join('|');
            }
        }
        return {};
    };
    return SimpleTypeElement;
}(Element));
exports.SimpleTypeElement = SimpleTypeElement;
var RestrictionElement = /** @class */ (function (_super) {
    __extends(RestrictionElement, _super);
    function RestrictionElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'all',
            'choice',
            'enumeration',
            'sequence',
        ]);
        return _this;
    }
    RestrictionElement.prototype.description = function (definitions, xmlns) {
        var children = this.children;
        var desc;
        for (var i = 0, child = void 0; child = children[i]; i++) {
            if (child instanceof SequenceElement || child instanceof ChoiceElement) {
                desc = child.description(definitions, xmlns);
                break;
            }
        }
        if (desc && this.$base) {
            var type = utils_1.splitQName(this.$base);
            var typeName = type.name;
            var ns = xmlns && xmlns[type.prefix] || definitions.xmlns[type.prefix];
            var schema_1 = definitions.schemas[ns];
            var typeElement_1 = schema_1 && (schema_1.complexTypes[typeName] || schema_1.types[typeName] || schema_1.elements[typeName]);
            desc.getBase = function () {
                return typeElement_1.description(definitions, schema_1.xmlns);
            };
            return desc;
        }
        // then simple element
        var base = this.$base ? this.$base + '|' : '';
        var restrictions = this.children.map(function (child) {
            return child.description();
        }).join(',');
        return [this.$base, restrictions].filter(Boolean).join('|');
    };
    return RestrictionElement;
}(Element));
exports.RestrictionElement = RestrictionElement;
var ExtensionElement = /** @class */ (function (_super) {
    __extends(ExtensionElement, _super);
    function ExtensionElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'all',
            'choice',
            'sequence',
        ]);
        return _this;
    }
    ExtensionElement.prototype.description = function (definitions, xmlns) {
        var desc = {};
        for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
            var child = _a[_i];
            if (child instanceof SequenceElement || child instanceof ChoiceElement) {
                desc = child.description(definitions, xmlns);
            }
        }
        if (this.$base) {
            var type = utils_1.splitQName(this.$base);
            var typeName = type.name;
            var ns = xmlns && xmlns[type.prefix] || definitions.xmlns[type.prefix];
            var schema = definitions.schemas[ns];
            if (typeName in Primitives) {
                return this.$base;
            }
            else {
                var typeElement = schema && (schema.complexTypes[typeName] ||
                    schema.types[typeName] || schema.elements[typeName]);
                if (typeElement) {
                    var base = typeElement.description(definitions, schema.xmlns);
                    desc = typeof base === 'string' ? base : _.defaults(base, desc);
                }
            }
        }
        return desc;
    };
    return ExtensionElement;
}(Element));
exports.ExtensionElement = ExtensionElement;
var ChoiceElement = /** @class */ (function (_super) {
    __extends(ChoiceElement, _super);
    function ChoiceElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'any',
            'choice',
            'element',
            'sequence',
        ]);
        return _this;
    }
    ChoiceElement.prototype.description = function (definitions, xmlns) {
        var choice = {};
        for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
            var child = _a[_i];
            var description = child.description(definitions, xmlns);
            for (var key in description) {
                choice[key] = description[key];
            }
        }
        return choice;
    };
    return ChoiceElement;
}(Element));
exports.ChoiceElement = ChoiceElement;
var EnumerationElement = /** @class */ (function (_super) {
    __extends(EnumerationElement, _super);
    function EnumerationElement() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    // no children
    EnumerationElement.prototype.description = function () {
        return this[this.valueKey];
    };
    return EnumerationElement;
}(Element));
exports.EnumerationElement = EnumerationElement;
var ComplexTypeElement = /** @class */ (function (_super) {
    __extends(ComplexTypeElement, _super);
    function ComplexTypeElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'all',
            'annotation',
            'choice',
            'complexContent',
            'sequence',
            'simpleContent',
        ]);
        return _this;
    }
    ComplexTypeElement.prototype.description = function (definitions, xmlns) {
        var children = this.children || [];
        for (var _i = 0, children_2 = children; _i < children_2.length; _i++) {
            var child = children_2[_i];
            if (child instanceof ChoiceElement ||
                child instanceof SequenceElement ||
                child instanceof AllElement ||
                child instanceof SimpleContentElement ||
                child instanceof ComplexContentElement) {
                return child.description(definitions, xmlns);
            }
        }
        return {};
    };
    return ComplexTypeElement;
}(Element));
exports.ComplexTypeElement = ComplexTypeElement;
var ComplexContentElement = /** @class */ (function (_super) {
    __extends(ComplexContentElement, _super);
    function ComplexContentElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'extension',
        ]);
        return _this;
    }
    ComplexContentElement.prototype.description = function (definitions, xmlns) {
        for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
            var child = _a[_i];
            if (child instanceof ExtensionElement) {
                return child.description(definitions, xmlns);
            }
        }
        return {};
    };
    return ComplexContentElement;
}(Element));
exports.ComplexContentElement = ComplexContentElement;
var SimpleContentElement = /** @class */ (function (_super) {
    __extends(SimpleContentElement, _super);
    function SimpleContentElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'extension',
        ]);
        return _this;
    }
    SimpleContentElement.prototype.description = function (definitions, xmlns) {
        for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
            var child = _a[_i];
            if (child instanceof ExtensionElement) {
                return child.description(definitions, xmlns);
            }
        }
        return {};
    };
    return SimpleContentElement;
}(Element));
exports.SimpleContentElement = SimpleContentElement;
var SequenceElement = /** @class */ (function (_super) {
    __extends(SequenceElement, _super);
    function SequenceElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'any',
            'choice',
            'element',
            'sequence',
        ]);
        return _this;
    }
    SequenceElement.prototype.description = function (definitions, xmlns) {
        var sequence = {};
        for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
            var child = _a[_i];
            if (child instanceof AnyElement) {
                continue;
            }
            var description = child.description(definitions, xmlns);
            for (var key in description) {
                sequence[key] = description[key];
            }
        }
        return sequence;
    };
    return SequenceElement;
}(Element));
exports.SequenceElement = SequenceElement;
var AllElement = /** @class */ (function (_super) {
    __extends(AllElement, _super);
    function AllElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'choice',
            'element',
        ]);
        return _this;
    }
    AllElement.prototype.description = function (definitions, xmlns) {
        var sequence = {};
        for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
            var child = _a[_i];
            if (child instanceof AnyElement) {
                continue;
            }
            var description = child.description(definitions, xmlns);
            for (var key in description) {
                sequence[key] = description[key];
            }
        }
        return sequence;
    };
    return AllElement;
}(Element));
exports.AllElement = AllElement;
var MessageElement = /** @class */ (function (_super) {
    __extends(MessageElement, _super);
    function MessageElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'part',
            'documentation',
        ]);
        _this.element = null;
        _this.parts = null;
        return _this;
    }
    MessageElement.prototype.postProcess = function (definitions) {
        var part = null;
        var children = this.children || [];
        for (var _i = 0, children_3 = children; _i < children_3.length; _i++) {
            var child = children_3[_i];
            if (child.name === 'part') {
                part = child;
                break;
            }
        }
        if (!part) {
            return;
        }
        if (part.$element) {
            var lookupTypes = [];
            delete this.parts;
            var nsName = utils_1.splitQName(part.$element);
            var ns = nsName.prefix;
            var schema = definitions.schemas[definitions.xmlns[ns]];
            this.element = schema.elements[nsName.name];
            if (!this.element) {
                debug(nsName.name + ' is not present in wsdl and cannot be processed correctly.');
                return;
            }
            this.element.targetNSAlias = ns;
            this.element.targetNamespace = definitions.xmlns[ns];
            // set the optional $lookupType to be used within `client#_invoke()` when
            // calling `wsdl#objectToDocumentXML()
            this.element.$lookupType = part.$element;
            var elementChildren = this.element.children;
            // get all nested lookup types (only complex types are followed)
            if (elementChildren.length > 0) {
                for (var _a = 0, elementChildren_1 = elementChildren; _a < elementChildren_1.length; _a++) {
                    var child = elementChildren_1[_a];
                    lookupTypes.push(this._getNestedLookupTypeString(child));
                }
            }
            // if nested lookup types where found, prepare them for furter usage
            if (lookupTypes.length > 0) {
                lookupTypes = lookupTypes.
                    join('_').
                    split('_').
                    filter(function removeEmptyLookupTypes(type) {
                    return type !== '^';
                });
                var schemaXmlns = definitions.schemas[this.element.targetNamespace].xmlns;
                for (var i = 0; i < lookupTypes.length; i++) {
                    lookupTypes[i] = this._createLookupTypeObject(lookupTypes[i], schemaXmlns);
                }
            }
            this.element.$lookupTypes = lookupTypes;
            if (this.element.$type) {
                var type = utils_1.splitQName(this.element.$type);
                var typeNs = schema.xmlns && schema.xmlns[type.prefix] || definitions.xmlns[type.prefix];
                if (typeNs) {
                    if (type.name in Primitives) {
                        // this.element = this.element.$type;
                    }
                    else {
                        // first check local mapping of ns alias to namespace
                        schema = definitions.schemas[typeNs];
                        var ctype = schema.complexTypes[type.name] || schema.types[type.name] || schema.elements[type.name];
                        if (ctype) {
                            this.parts = ctype.description(definitions, schema.xmlns);
                        }
                    }
                }
            }
            else {
                var method = this.element.description(definitions, schema.xmlns);
                this.parts = method[nsName.name];
            }
            this.children.splice(0, 1);
        }
        else {
            // rpc encoding
            this.parts = {};
            delete this.element;
            for (var i = 0; part = this.children[i]; i++) {
                if (part.name === 'documentation') {
                    // <wsdl:documentation can be present under <wsdl:message>
                    continue;
                }
                assert_1.ok(part.name === 'part', 'Expected part element');
                var nsName = utils_1.splitQName(part.$type);
                var ns = definitions.xmlns[nsName.prefix];
                var type = nsName.name;
                var schemaDefinition = definitions.schemas[ns];
                if (typeof schemaDefinition !== 'undefined') {
                    this.parts[part.$name] = definitions.schemas[ns].types[type] || definitions.schemas[ns].complexTypes[type];
                }
                else {
                    this.parts[part.$name] = part.$type;
                }
                if (typeof this.parts[part.$name] === 'object') {
                    this.parts[part.$name].prefix = nsName.prefix;
                    this.parts[part.$name].xmlns = ns;
                }
                this.children.splice(i--, 1);
            }
        }
        this.deleteFixedAttrs();
    };
    MessageElement.prototype.description = function (definitions) {
        if (this.element) {
            return this.element && this.element.description(definitions);
        }
        var desc = {};
        desc[this.$name] = this.parts;
        return desc;
    };
    /**
     * Takes a given namespaced String(for example: 'alias:property') and creates a lookupType
     * object for further use in as first (lookup) `parameterTypeObj` within the `objectToXML`
     * method and provides an entry point for the already existing code in `findChildSchemaObject`.
     *
     * @method _createLookupTypeObject
     * @param {String}            nsString          The NS String (for example "alias:type").
     * @param {Object}            xmlns       The fully parsed `wsdl` definitions object (including all schemas).
     * @returns {Object}
     * @private
     */
    MessageElement.prototype._createLookupTypeObject = function (nsString, xmlns) {
        var splittedNSString = utils_1.splitQName(nsString);
        var nsAlias = splittedNSString.prefix;
        var splittedName = splittedNSString.name.split('#');
        var type = splittedName[0];
        var name = splittedName[1];
        return {
            $namespace: xmlns[nsAlias],
            $type: nsAlias + ':' + type,
            $name: name
        };
    };
    /**
     * Iterates through the element and every nested child to find any defined `$type`
     * property and returns it in a underscore ('_') separated String (using '^' as default
     * value if no `$type` property was found).
     *
     * @method _getNestedLookupTypeString
     * @param {Object}            element         The element which (probably) contains nested `$type` values.
     * @returns {String}
     * @private
     */
    MessageElement.prototype._getNestedLookupTypeString = function (element) {
        var _this = this;
        var resolvedType = '^';
        var excluded = this.ignoredNamespaces.concat('xs'); // do not process $type values wich start with
        if (element.hasOwnProperty('$type') && typeof element.$type === 'string') {
            if (excluded.indexOf(element.$type.split(':')[0]) === -1) {
                resolvedType += ('_' + element.$type + '#' + element.$name);
            }
        }
        if (element.children.length > 0) {
            element.children.forEach(function (child) {
                var resolvedChildType = _this._getNestedLookupTypeString(child).replace(/\^_/, '');
                if (resolvedChildType && typeof resolvedChildType === 'string') {
                    resolvedType += ('_' + resolvedChildType);
                }
            });
        }
        return resolvedType;
    };
    return MessageElement;
}(Element));
exports.MessageElement = MessageElement;
var DocumentationElement = /** @class */ (function (_super) {
    __extends(DocumentationElement, _super);
    function DocumentationElement() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return DocumentationElement;
}(Element));
exports.DocumentationElement = DocumentationElement;
var SchemaElement = /** @class */ (function (_super) {
    __extends(SchemaElement, _super);
    function SchemaElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'complexType',
            'element',
            'import',
            'include',
            'simpleType',
        ]);
        _this.complexTypes = {};
        _this.types = {};
        _this.elements = {};
        _this.includes = [];
        return _this;
    }
    SchemaElement.prototype.merge = function (source) {
        var _this = this;
        assert_1.ok(source instanceof SchemaElement);
        _.merge(this.complexTypes, source.complexTypes);
        _.merge(this.types, source.types);
        _.merge(this.elements, source.elements);
        _.merge(this.xmlns, source.xmlns);
        // Merge attributes from source without overwriting our's
        _.merge(this, _.pickBy(source, function (value, key) {
            return key.startsWith('$') && !_this.hasOwnProperty(key);
        }));
        return this;
    };
    SchemaElement.prototype.addChild = function (child) {
        if (child.$name in Primitives) {
            return;
        }
        if (child instanceof IncludeElement || child instanceof ImportElement) {
            var location_1 = child.$schemaLocation || child.$location;
            if (location_1) {
                this.includes.push({
                    namespace: child.$namespace || child.$targetNamespace || this.$targetNamespace,
                    location: location_1
                });
            }
        }
        else if (child instanceof ComplexTypeElement) {
            this.complexTypes[child.$name] = child;
        }
        else if (child instanceof ElementElement) {
            this.elements[child.$name] = child;
        }
        else if (child instanceof SimpleTypeElement) {
            this.types[child.$name] = child;
        }
        this.children.pop();
        // child.deleteFixedAttrs();
    };
    return SchemaElement;
}(Element));
exports.SchemaElement = SchemaElement;
var TypesElement = /** @class */ (function (_super) {
    __extends(TypesElement, _super);
    function TypesElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'documentation',
            'schema',
        ]);
        _this.schemas = {};
        return _this;
    }
    // fix#325
    TypesElement.prototype.addChild = function (child) {
        assert_1.ok(child instanceof SchemaElement);
        var targetNamespace = child.$targetNamespace;
        if (!this.schemas.hasOwnProperty(targetNamespace)) {
            this.schemas[targetNamespace] = child;
        }
        else {
            console.error('Target-Namespace "' + targetNamespace + '" already in use by another Schema!');
        }
    };
    return TypesElement;
}(Element));
exports.TypesElement = TypesElement;
var OperationElement = /** @class */ (function (_super) {
    __extends(OperationElement, _super);
    function OperationElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'documentation',
            'fault',
            'input',
            'operation',
            'output',
        ]);
        _this.input = null;
        _this.output = null;
        _this.inputSoap = null;
        _this.outputSoap = null;
        _this.style = '';
        _this.soapAction = '';
        return _this;
    }
    OperationElement.prototype.addChild = function (child) {
        if (child instanceof OperationElement) {
            this.soapAction = child.$soapAction || '';
            this.style = child.$style || '';
            this.children.pop();
        }
    };
    OperationElement.prototype.postProcess = function (definitions, tag) {
        var children = this.children;
        for (var i = 0, child = void 0; child = children[i]; i++) {
            if (child.name !== 'input' && child.name !== 'output') {
                continue;
            }
            if (tag === 'binding') {
                this[child.name] = child;
                children.splice(i--, 1);
                continue;
            }
            var messageName = utils_1.splitQName(child.$message).name;
            var message = definitions.messages[messageName];
            message.postProcess(definitions);
            if (message.element) {
                definitions.messages[message.element.$name] = message;
                this[child.name] = message.element;
            }
            else {
                this[child.name] = message;
            }
            children.splice(i--, 1);
        }
        this.deleteFixedAttrs();
    };
    OperationElement.prototype.description = function (definitions) {
        var inputDesc = this.input ? this.input.description(definitions) : null;
        var outputDesc = this.output ? this.output.description(definitions) : null;
        return {
            input: inputDesc && inputDesc[Object.keys(inputDesc)[0]],
            output: outputDesc && outputDesc[Object.keys(outputDesc)[0]]
        };
    };
    return OperationElement;
}(Element));
exports.OperationElement = OperationElement;
var PortTypeElement = /** @class */ (function (_super) {
    __extends(PortTypeElement, _super);
    function PortTypeElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'documentation',
            'operation',
        ]);
        _this.methods = {};
        return _this;
    }
    PortTypeElement.prototype.postProcess = function (definitions) {
        var children = this.children;
        if (typeof children === 'undefined') {
            return;
        }
        for (var i = 0, child = void 0; child = children[i]; i++) {
            if (child.name !== 'operation') {
                continue;
            }
            child.postProcess(definitions, 'portType');
            this.methods[child.$name] = child;
            children.splice(i--, 1);
        }
        delete this.$name;
        this.deleteFixedAttrs();
    };
    PortTypeElement.prototype.description = function (definitions) {
        var methods = {};
        for (var name_1 in this.methods) {
            var method = this.methods[name_1];
            methods[name_1] = method.description(definitions);
        }
        return methods;
    };
    return PortTypeElement;
}(Element));
exports.PortTypeElement = PortTypeElement;
var BindingElement = /** @class */ (function (_super) {
    __extends(BindingElement, _super);
    function BindingElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'binding',
            'documentation',
            'operation',
            'SecuritySpec',
        ]);
        _this.transport = '';
        _this.style = '';
        _this.methods = {};
        return _this;
    }
    BindingElement.prototype.addChild = function (child) {
        if (child.name === 'binding') {
            this.transport = child.$transport;
            this.style = child.$style;
            this.children.pop();
        }
    };
    BindingElement.prototype.postProcess = function (definitions) {
        var type = utils_1.splitQName(this.$type).name;
        var portType = definitions.portTypes[type];
        var style = this.style;
        var children = this.children;
        if (portType) {
            portType.postProcess(definitions);
            this.methods = portType.methods;
            for (var i = 0, child = void 0; child = children[i]; i++) {
                if (child.name !== 'operation') {
                    continue;
                }
                child.postProcess(definitions, 'binding');
                children.splice(i--, 1);
                child.style || (child.style = style);
                var method = this.methods[child.$name];
                if (method) {
                    method.style = child.style;
                    method.soapAction = child.soapAction;
                    method.inputSoap = child.input || null;
                    method.outputSoap = child.output || null;
                    method.inputSoap && method.inputSoap.deleteFixedAttrs();
                    method.outputSoap && method.outputSoap.deleteFixedAttrs();
                }
            }
        }
        delete this.$name;
        delete this.$type;
        this.deleteFixedAttrs();
    };
    BindingElement.prototype.description = function (definitions) {
        var methods = {};
        for (var name_2 in this.methods) {
            var method = this.methods[name_2];
            methods[name_2] = method.description(definitions);
        }
        return methods;
    };
    return BindingElement;
}(Element));
exports.BindingElement = BindingElement;
var PortElement = /** @class */ (function (_super) {
    __extends(PortElement, _super);
    function PortElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'address',
            'documentation',
        ]);
        _this.location = null;
        return _this;
    }
    PortElement.prototype.addChild = function (child) {
        if (child.name === 'address' && typeof (child.$location) !== 'undefined') {
            this.location = child.$location;
        }
    };
    return PortElement;
}(Element));
exports.PortElement = PortElement;
var ServiceElement = /** @class */ (function (_super) {
    __extends(ServiceElement, _super);
    function ServiceElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'documentation',
            'port',
        ]);
        _this.ports = {};
        return _this;
    }
    ServiceElement.prototype.postProcess = function (definitions) {
        var children = this.children;
        var bindings = definitions.bindings;
        if (children && children.length > 0) {
            for (var i = 0, child = void 0; child = children[i]; i++) {
                if (child.name !== 'port') {
                    continue;
                }
                var bindingName = utils_1.splitQName(child.$binding).name;
                var binding = bindings[bindingName];
                if (binding) {
                    binding.postProcess(definitions);
                    this.ports[child.$name] = {
                        location: child.location,
                        binding: binding
                    };
                    children.splice(i--, 1);
                }
            }
        }
        delete this.$name;
        this.deleteFixedAttrs();
    };
    ServiceElement.prototype.description = function (definitions) {
        var ports = {};
        for (var name_3 in this.ports) {
            var port = this.ports[name_3];
            ports[name_3] = port.binding.description(definitions);
        }
        return ports;
    };
    return ServiceElement;
}(Element));
exports.ServiceElement = ServiceElement;
var DefinitionsElement = /** @class */ (function (_super) {
    __extends(DefinitionsElement, _super);
    function DefinitionsElement() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.allowedChildren = buildAllowedChildren([
            'binding',
            'documentation',
            'import',
            'message',
            'portType',
            'service',
            'types',
        ]);
        _this.messages = {};
        _this.portTypes = {};
        _this.bindings = {};
        _this.services = {};
        _this.schemas = {};
        _this.descriptions = {
            types: {}
        };
        return _this;
    }
    DefinitionsElement.prototype.init = function () {
        if (this.name !== 'definitions') {
            this.unexpected(this.nsName);
        }
    };
    DefinitionsElement.prototype.addChild = function (child) {
        if (child instanceof TypesElement) {
            // Merge types.schemas into definitions.schemas
            _.merge(this.schemas, child.schemas);
        }
        else if (child instanceof MessageElement) {
            this.messages[child.$name] = child;
        }
        else if (child.name === 'import') {
            var schemaElement = new SchemaElement(child.$namespace, {});
            schemaElement.init();
            this.schemas[child.$namespace] = schemaElement;
            this.schemas[child.$namespace].addChild(child);
        }
        else if (child instanceof PortTypeElement) {
            this.portTypes[child.$name] = child;
        }
        else if (child instanceof BindingElement) {
            if (child.transport === 'http://schemas.xmlsoap.org/soap/http' ||
                child.transport === 'http://www.w3.org/2003/05/soap/bindings/HTTP/') {
                this.bindings[child.$name] = child;
            }
        }
        else if (child instanceof ServiceElement) {
            this.services[child.$name] = child;
        }
        else if (child instanceof DocumentationElement) {
        }
        this.children.pop();
    };
    return DefinitionsElement;
}(Element));
exports.DefinitionsElement = DefinitionsElement;
var BodyElement = /** @class */ (function (_super) {
    __extends(BodyElement, _super);
    function BodyElement() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return BodyElement;
}(Element));
exports.BodyElement = BodyElement;
var IncludeElement = /** @class */ (function (_super) {
    __extends(IncludeElement, _super);
    function IncludeElement() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return IncludeElement;
}(Element));
exports.IncludeElement = IncludeElement;
var ImportElement = /** @class */ (function (_super) {
    __extends(ImportElement, _super);
    function ImportElement() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return ImportElement;
}(Element));
exports.ImportElement = ImportElement;
var ElementTypeMap = {
    // group: [GroupElement, 'element group'],
    all: AllElement,
    any: AnyElement,
    binding: BindingElement,
    body: BodyElement,
    choice: ChoiceElement,
    complexContent: ComplexContentElement,
    complexType: ComplexTypeElement,
    definitions: DefinitionsElement,
    documentation: DocumentationElement,
    element: ElementElement,
    enumeration: EnumerationElement,
    extension: ExtensionElement,
    fault: Element,
    "import": ImportElement,
    include: IncludeElement,
    input: InputElement,
    message: MessageElement,
    operation: OperationElement,
    output: OutputElement,
    port: PortElement,
    portType: PortTypeElement,
    restriction: RestrictionElement,
    schema: SchemaElement,
    sequence: SequenceElement,
    service: ServiceElement,
    simpleContent: SimpleContentElement,
    simpleType: SimpleTypeElement,
    types: TypesElement
};
function buildAllowedChildren(elementList) {
    var rtn = {};
    for (var _i = 0, elementList_1 = elementList; _i < elementList_1.length; _i++) {
        var element = elementList_1[_i];
        rtn[element.replace(/^_/, '')] = ElementTypeMap[element] || Element;
    }
    return rtn;
}
//# sourceMappingURL=elements.js.map