"use strict";
var __extends = (this && this.__extends) || (function () {
    var 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 function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var ti = require("./nominal-interfaces");
var tsInterfaces = require("./typesystem-interfaces");
var _ = require("./utils");
var typesystem_1 = require("./typesystem");
var messageRegistry = require("../../resources/errorMessages");
var extraInjections = [];
function registerInjector(i) {
    extraInjections.push(i);
}
exports.registerInjector = registerInjector;
var Adaptable = /** @class */ (function () {
    function Adaptable() {
        var _this = this;
        this.adapters = [];
        extraInjections.forEach(function (x) { return x.inject(_this); });
    }
    Adaptable.isInstance = function (instance) {
        if (instance != null && instance.getClassIdentifier
            && typeof (instance.getClassIdentifier) == "function") {
            for (var _i = 0, _a = instance.getClassIdentifier(); _i < _a.length; _i++) {
                var currentIdentifier = _a[_i];
                if (currentIdentifier == Adaptable.CLASS_IDENTIFIER_Adaptable)
                    return true;
            }
        }
        return false;
    };
    Adaptable.prototype.getClassIdentifier = function () {
        var superIdentifiers = [];
        return superIdentifiers.concat(Adaptable.CLASS_IDENTIFIER_Adaptable);
    };
    Adaptable.prototype.addAdapter = function (q) {
        this.adapters.push(q);
    };
    Adaptable.prototype.getAdapter = function (adapterType) {
        var result = null;
        this.adapters.forEach(function (x) {
            if (x instanceof adapterType) {
                result = x;
            }
        });
        return result;
    };
    Adaptable.prototype.getAdapters = function () {
        return this.adapters;
    };
    Adaptable.CLASS_IDENTIFIER_Adaptable = "nominal-types.Adaptable";
    return Adaptable;
}());
exports.Adaptable = Adaptable;
var Described = /** @class */ (function (_super) {
    __extends(Described, _super);
    function Described(_name, _description) {
        if (_description === void 0) { _description = ""; }
        var _this = _super.call(this) || this;
        _this._name = _name;
        _this._description = _description;
        _this._tags = [];
        _this._annotations = [];
        return _this;
    }
    Described.prototype.nameId = function () { return this._name; };
    Described.prototype.description = function () { return this._description; };
    Described.isInstance = function (instance) {
        if (instance != null && instance.getClassIdentifier
            && typeof (instance.getClassIdentifier) == "function") {
            for (var _i = 0, _a = instance.getClassIdentifier(); _i < _a.length; _i++) {
                var currentIdentifier = _a[_i];
                if (currentIdentifier == Described.CLASS_IDENTIFIER_Described)
                    return true;
            }
        }
        return false;
    };
    Described.prototype.getClassIdentifier = function () {
        var superIdentifiers = _super.prototype.getClassIdentifier.call(this);
        return superIdentifiers.concat(Described.CLASS_IDENTIFIER_Described);
    };
    Described.prototype.addAnnotation = function (a) {
        this._annotations.push(a);
    };
    Described.prototype.removeAnnotation = function (a) {
        this._annotations = this._annotations.filter(function (x) { return x != a; });
    };
    Described.prototype.annotations = function () {
        return [].concat(this._annotations);
    };
    Described.prototype.tags = function () {
        return this._tags;
    };
    Described.prototype.withDescription = function (d) {
        this._description = d;
        return this;
    };
    Described.prototype.setName = function (name) {
        this._name = name;
    };
    Described.CLASS_IDENTIFIER_Described = "nominal-types.Described";
    return Described;
}(Adaptable));
exports.Described = Described;
var Annotation = /** @class */ (function (_super) {
    __extends(Annotation, _super);
    function Annotation(type, parameters) {
        var _this = _super.call(this, type.nameId()) || this;
        _this.type = type;
        _this.parameters = parameters;
        return _this;
    }
    Annotation.prototype.parameterNames = function () {
        return Object.keys(this.parameters);
    };
    Annotation.prototype.parameter = function (name) {
        return this.parameters[name];
    };
    Annotation.prototype.getType = function () {
        return this.type;
    };
    return Annotation;
}(Described));
exports.Annotation = Annotation;
var Empty = /** @class */ (function () {
    function Empty() {
    }
    return Empty;
}());
exports.Empty = Empty;
var EmptyUniverse = /** @class */ (function () {
    function EmptyUniverse() {
    }
    EmptyUniverse.prototype.type = function (name) {
        return null;
    };
    EmptyUniverse.prototype.version = function () {
        return "Empty";
    };
    EmptyUniverse.prototype.types = function () {
        return [];
    };
    EmptyUniverse.prototype.matched = function () {
        return {};
    };
    return EmptyUniverse;
}());
var emptyUniverse = new EmptyUniverse();
var ebuilder = require("./exampleBuilder");
var AbstractType = /** @class */ (function (_super) {
    __extends(AbstractType, _super);
    function AbstractType(_name, _universe, _path) {
        if (_universe === void 0) { _universe = emptyUniverse; }
        if (_path === void 0) { _path = ""; }
        var _this = _super.call(this, _name) || this;
        _this._universe = _universe;
        _this._path = _path;
        _this._customProperties = [];
        _this._facets = [];
        _this._superTypes = [];
        _this._subTypes = [];
        _this._requirements = [];
        _this._fixedFacets = {};
        _this._fixedBuildInFacets = {};
        _this.uc = false;
        return _this;
    }
    AbstractType.prototype.properties = function () {
        return [];
    };
    AbstractType.prototype.externalInHierarchy = function () {
        var x = this.allSuperTypes();
        var res = null;
        x.forEach(function (y) {
            if (y instanceof ExternalType) {
                res = y;
            }
        });
        return res;
    };
    AbstractType.prototype.addFacet = function (q) {
        this._facets.push(q);
    };
    AbstractType.prototype.validate = function (x) {
        if (!this._validator) {
            throw new Error(messageRegistry.VALIDATE_ONLY_ON_RUNTIME_TYPES_INSTANCES.message);
        }
        return this._validator(x);
    };
    AbstractType.prototype.allFacets = function (ps) {
        if (ps === void 0) { ps = {}; }
        if (this._allFacets) {
            return this._allFacets;
        }
        if (ps[this.nameId()]) {
            return [];
        }
        ps[this.typeId()] = this;
        var n = {};
        if (this.superTypes().length > 0) {
            this.superTypes().forEach(function (x) {
                if (x instanceof AbstractType) {
                    x.allFacets(ps).forEach(function (y) { return n[y.nameId()] = y; });
                }
            });
        }
        this._facets.forEach(function (x) { return n[x.nameId()] = x; });
        //this.properties().forEach(x=>n[x.nameId()]=x);
        this._allFacets = Object.keys(n).map(function (x) { return n[x]; });
        return this._allFacets;
    };
    AbstractType.prototype.facets = function () {
        return [].concat(this._facets);
    };
    AbstractType.prototype.facet = function (name) {
        return _.find(this.allFacets(), function (x) { return x.nameId() == name; });
    };
    AbstractType.prototype.typeId = function () {
        return this.nameId();
    };
    AbstractType.prototype.allProperties = function (ps) {
        if (ps === void 0) { ps = {}; }
        if (this._props) {
            return this._props;
        }
        var uniqueTypeId = getUniqueTypeId(this);
        if (ps[uniqueTypeId]) {
            return [];
        }
        ps[uniqueTypeId] = this;
        var n = {};
        if (this.superTypes().length > 0) {
            this.superTypes().forEach(function (x) {
                if (x instanceof AbstractType) {
                    x.allProperties(ps).forEach(function (y) { return n[y.nameId()] = y; });
                }
                else {
                    x.allProperties().forEach(function (y) { return n[y.nameId()] = y; });
                }
            });
        }
        for (var x in this.fixedFacets()) {
            delete n[x];
        }
        this.properties().forEach(function (x) { return n[x.nameId()] = x; });
        this._props = Object.keys(n).map(function (x) { return n[x]; });
        return this._props;
    };
    AbstractType.prototype.property = function (propName) {
        return _.find(this.allProperties(), function (x) { return x.nameId() == propName; });
    };
    AbstractType.prototype.hasValueTypeInHierarchy = function () {
        return _.find(this.allSuperTypes(), function (x) {
            var mm = x;
            if (mm.uc) {
                return false;
            }
            mm.uc = true;
            try {
                return x.hasValueTypeInHierarchy();
            }
            finally {
                mm.uc = false;
            }
        }) != null;
    };
    AbstractType.prototype.isAnnotationType = function () {
        return false;
    };
    AbstractType.prototype.hasStructure = function () {
        return false;
    };
    AbstractType.prototype.key = function () {
        if (this._key) {
            return this._key;
        }
        if (this._universe) {
            this._key = this.universe().matched()[this.nameId()];
            if (!this._key) {
                return null;
            }
        }
        return this._key;
    };
    AbstractType.prototype.hasArrayInHierarchy = function () {
        var arr = _.find(this.allSuperTypes(), function (x) { return x instanceof Array; }) != null;
        return arr;
    };
    AbstractType.prototype.arrayInHierarchy = function () {
        var x = this.allSuperTypes();
        var res = null;
        x.forEach(function (y) {
            if (y instanceof Array) {
                res = y;
            }
        });
        return res;
    };
    AbstractType.prototype.unionInHierarchy = function () {
        var x = this.allSuperTypes();
        var res = null;
        x.forEach(function (y) {
            if (y instanceof Union) {
                res = y;
            }
        });
        return res;
    };
    AbstractType.prototype.hasExternalInHierarchy = function () {
        return _.find(this.allSuperTypes(), function (x) {
            var mm = x;
            if (mm.uc) {
                return false;
            }
            mm.uc = true;
            try {
                return x instanceof ExternalType;
            }
            finally {
                mm.uc = false;
            }
        }) != null;
    };
    AbstractType.prototype.hasUnionInHierarchy = function () {
        return _.find(this.allSuperTypes(), function (x) {
            var mm = x;
            if (mm.uc) {
                return false;
            }
            mm.uc = true;
            try {
                return x.hasUnionInHierarchy();
            }
            finally {
                mm.uc = false;
            }
        }) != null;
    };
    AbstractType.prototype.fixFacet = function (name, v, builtIn) {
        if (builtIn === void 0) { builtIn = false; }
        if (builtIn) {
            this._fixedBuildInFacets[name] = v;
        }
        else {
            this._fixedFacets[name] = v;
        }
    };
    /**
     * @deprecated
     */
    AbstractType.prototype.getFixedFacets = function () {
        return this.fixedFacets();
    };
    AbstractType.prototype.fixedFacets = function () {
        return this.collectFixedFacets(false);
    };
    AbstractType.prototype.fixedBuiltInFacets = function () {
        return this.collectFixedFacets(true);
    };
    AbstractType.prototype.collectFixedFacets = function (builtIn) {
        var facetsMap = builtIn ? this._fixedBuildInFacets : this._fixedFacets;
        var result = {};
        for (var _i = 0, _a = Object.keys(facetsMap); _i < _a.length; _i++) {
            var q = _a[_i];
            result[q] = facetsMap[q];
        }
        this.contributeFacets(result);
        return result;
    };
    AbstractType.prototype.allFixedFacets = function () {
        return this.collectAllFixedFacets(false);
    };
    AbstractType.prototype.allFixedBuiltInFacets = function () {
        return this.collectAllFixedFacets(true);
    };
    AbstractType.prototype.collectAllFixedFacets = function (builtIn) {
        if (builtIn && this._abf) {
            return this._abf;
        }
        else if (!builtIn && this._af) {
            return this._af;
        }
        var sp = this.allSuperTypes();
        sp.push(this);
        var mm = {};
        sp.forEach(function (x) {
            var ff = builtIn ? x.fixedBuiltInFacets() : x.fixedFacets();
            for (var _i = 0, _a = Object.keys(ff); _i < _a.length; _i++) {
                var key = _a[_i];
                mm[key] = ff[key];
            }
        });
        this._af = mm;
        return mm;
    };
    AbstractType.prototype.contributeFacets = function (x) {
    };
    AbstractType.prototype.getPath = function () {
        return this._path;
    };
    AbstractType.prototype.setNameAtRuntime = function (name) {
        this._nameAtRuntime = name;
    };
    AbstractType.prototype.getNameAtRuntime = function () {
        return this._nameAtRuntime;
    };
    AbstractType.prototype.universe = function () {
        if (!this._universe) {
            return new EmptyUniverse();
        }
        return this._universe;
    };
    AbstractType.prototype.superTypes = function () {
        return [].concat(this._superTypes);
    };
    AbstractType.prototype.isAssignableFrom = function (typeName) {
        if (this.nameId() == typeName) {
            if (this.isUserDefined()) {
                return false;
            }
            return true;
        }
        var currentSuperTypes = this.allSuperTypes();
        for (var i = 0; i < currentSuperTypes.length; i++) {
            if (currentSuperTypes[i].nameId() == typeName) {
                return true;
            }
        }
        return false;
    };
    AbstractType.prototype.annotationType = function () {
        return null;
    };
    AbstractType.prototype.subTypes = function () {
        return [].concat(this._subTypes);
    };
    AbstractType.prototype.allSubTypes = function () {
        var rs = [];
        this.subTypes().forEach(function (x) {
            rs.push(x);
            rs = rs.concat(x.allSubTypes());
        });
        return _.unique(rs);
    };
    AbstractType.prototype.allSuperTypes = function () {
        if (this._allSupers) {
            return this._allSupers;
        }
        var rs = [];
        this.allSuperTypesRecurrent(this, {}, rs);
        this._allSupers = _.unique(rs);
        return this._allSupers;
    };
    AbstractType.prototype.allSuperTypesRecurrent = function (t, m, result) {
        var _this = this;
        t.superTypes().forEach(function (x) {
            var uniqueTypeId = getUniqueTypeId(x);
            if (!uniqueTypeId) {
                var adapter = x.getAdapter(typesystem_1.InheritedType);
                uniqueTypeId = (adapter && (adapter.id() + '')) || '';
            }
            if (!m[uniqueTypeId]) {
                result.push(x);
                m[uniqueTypeId] = x;
                _this.allSuperTypesRecurrent(x, m, result);
            }
        });
    };
    AbstractType.prototype.addSuperType = function (q) {
        q._subTypes.push(this);
        this._superTypes.push(q);
    };
    AbstractType.prototype.addRequirement = function (name, value) {
        this._requirements.push(new ti.ValueRequirement(name, value));
    };
    //FIXME simplify it
    AbstractType.prototype.valueRequirements = function () {
        return this._requirements;
    };
    AbstractType.prototype.requiredProperties = function () {
        return this.allProperties().filter(function (x) { return x.isRequired(); });
    };
    AbstractType.prototype.printDetails = function (indent, settings) {
        var _this = this;
        if (!indent) {
            indent = "";
        }
        if (!settings) {
            settings = {
                hideProperties: false,
                hideSuperTypeProperties: false,
                printStandardSuperclasses: false
            };
        }
        var standardIndent = "  ";
        var result = "";
        var className = this.getTypeClassName();
        result += indent + this.nameId() + "[" + className + "]" + "\n";
        var properties = this.properties();
        if (properties && properties.length > 0 && !settings.hideProperties) {
            result += indent + standardIndent + "Properties:\n";
            properties.forEach(function (property) {
                var propertyType = "";
                var propertyRange = property.range();
                if (propertyRange instanceof Described) {
                    propertyType += propertyRange.nameId();
                }
                if (propertyRange instanceof AbstractType) {
                    propertyType += "[";
                    propertyType += propertyRange.getTypeClassName();
                    propertyType += "]";
                }
                result += indent + standardIndent + standardIndent + property.nameId() + " : " + propertyType + "\n";
            });
        }
        var superTypes = this.superTypes();
        var filteredSuperTypes = superTypes;
        if (superTypes && !settings.printStandardSuperclasses) {
            filteredSuperTypes = _.filter(superTypes, function (superType) {
                var name = superType instanceof Described ? superType.nameId() : "";
                var type = superType instanceof AbstractType ?
                    superType.getTypeClassName() : "";
                return !_this.isStandardSuperclass(name, type);
            });
        }
        if (filteredSuperTypes && filteredSuperTypes.length > 0) {
            result += indent + standardIndent + "Super types:\n";
            filteredSuperTypes.forEach(function (superType) {
                result += superType.printDetails(indent + standardIndent + standardIndent, {
                    hideProperties: settings.hideSuperTypeProperties,
                    hideSuperTypeProperties: settings.hideSuperTypeProperties,
                    printStandardSuperclasses: settings.printStandardSuperclasses
                });
            });
        }
        return result;
    };
    AbstractType.prototype.getTypeClassName = function () {
        return this.constructor.toString().match(/\w+/g)[1];
    };
    AbstractType.prototype.isStandardSuperclass = function (nameId, className) {
        if (nameId === "TypeDeclaration" && className === "NodeClass")
            return true;
        if (nameId === "ObjectTypeDeclaration" && className === "NodeClass")
            return true;
        if (nameId === "RAMLLanguageElement" && className === "NodeClass")
            return true;
        return false;
    };
    /**
     * Returns example for this type.
     * Returned example should be tested for being empty and being expandable.
     */
    AbstractType.prototype.examples = function (collectFromSupertype) {
        return ebuilder.exampleFromNominal(this, collectFromSupertype);
    };
    /**
     * Returns whether this type contain genuine user defined type in its hierarchy.
     * Genuine user defined type is a type user intentionally defined and filled with
     * properties or facets, or having user-defined name as opposed to a synthetic user-defined type.
     */
    AbstractType.prototype.isGenuineUserDefinedType = function () {
        if (this.buildIn)
            return false;
        if (this.properties() && this.properties().length > 0)
            return true;
        var facets = this.fixedFacets();
        if (facets && Object.keys(facets).length > 0)
            return true;
        var builtInFacets = this.fixedBuiltInFacets();
        if (builtInFacets && Object.keys(builtInFacets).length > 0)
            return true;
        return this.isTopLevel() && this.nameId() && this.nameId().length > 0;
    };
    /**
     * Returns nearest genuine user-define type in the hierarchy.
     * Genuine user defined type is a type user intentionally defined and filled with
     * properties or facets, or having user-defined name as opposed to a synthetic user-defined type.
     */
    AbstractType.prototype.genuineUserDefinedTypeInHierarchy = function () {
        if (this.isGenuineUserDefinedType())
            return this;
        var result = null;
        var allSuperTypes = this.allSuperTypes();
        allSuperTypes.forEach(function (currentSuperType) {
            if (!result && currentSuperType.isGenuineUserDefinedType()) {
                result = currentSuperType;
            }
        });
        return result;
    };
    /**
     * Returns whether this type contain genuine user defined type in its hierarchy.
     * Genuine user defined type is a type user intentionally defined and filled with
     * properties or facets, or having user-defined name as opposed to a synthetic user-defined type.
     */
    AbstractType.prototype.hasGenuineUserDefinedTypeInHierarchy = function () {
        return _.find(this.allSuperTypes(), function (x) {
            var mm = x;
            if (mm.uc) {
                return false;
            }
            mm.uc = true;
            try {
                return x.isGenuineUserDefinedType();
            }
            finally {
                mm.uc = false;
            }
        }) != null;
    };
    AbstractType.prototype.customProperties = function () {
        return [].concat(this._customProperties);
    };
    AbstractType.prototype.allCustomProperties = function () {
        var props = [];
        this.superTypes().forEach(function (x) { return props = props.concat(x.allCustomProperties()); });
        props = props.concat(this.customProperties());
        return props;
    };
    AbstractType.prototype.registerCustomProperty = function (p) {
        if (p.domain() != this) {
            throw new Error(messageRegistry.SHOULD_BE_ALREADY_OWNED.message);
        }
        if (this._customProperties.indexOf(p) != -1) {
            throw new Error(messageRegistry.ALREADY_INCLUDED.message);
        }
        this._customProperties.push(p);
    };
    AbstractType.prototype.setCustom = function (val) {
        this._isCustom = val;
    };
    AbstractType.prototype.isCustom = function () {
        return this._isCustom;
    };
    AbstractType.prototype.isUnion = function () {
        return false;
    };
    AbstractType.prototype.union = function () {
        return null;
    };
    AbstractType.prototype.isExternal = function () {
        return false;
    };
    AbstractType.prototype.external = function () {
        return null;
    };
    AbstractType.prototype.isArray = function () {
        return false;
    };
    AbstractType.prototype.isObject = function () {
        if (this.nameId() == "object") {
            return true;
        }
        for (var _i = 0, _a = this.allSuperTypes(); _i < _a.length; _i++) {
            var t = _a[_i];
            if (t.isObject()) {
                return true;
            }
        }
        return false;
    };
    AbstractType.prototype.array = function () {
        return null;
    };
    AbstractType.prototype.isValueType = function () {
        return false;
    };
    AbstractType.prototype.kind = function () {
        var result = [];
        if (this.isObject()) {
            result.push("object");
        }
        if (this.isArray()) {
            result.push("array");
        }
        if (this.isValueType()) {
            result.push("value");
        }
        if (this.isUnion()) {
            result.push("union");
        }
        if (this.isAnnotationType()) {
            result.push("annotation");
        }
        if (this.isExternal()) {
            result.push("external");
        }
        return result;
    };
    AbstractType.prototype.isBuiltIn = function () {
        return this.buildIn;
    };
    AbstractType.prototype.setBuiltIn = function (builtIn) {
        this.buildIn = builtIn;
    };
    AbstractType.prototype.isTopLevel = function () {
        //TODO determine whether "topLevel" actually means a simple top-level type and
        //this flag is absent due to a bug
        if (this.getExtra(tsInterfaces.DEFINED_IN_TYPES_EXTRA) || this.getExtra(tsInterfaces.TOP_LEVEL_EXTRA))
            return true;
        return false;
    };
    AbstractType.prototype.isUserDefined = function () {
        return this.getExtra(tsInterfaces.USER_DEFINED_EXTRA) && !this.buildIn;
    };
    AbstractType.prototype.putExtra = function (extraName, value) {
        var extraAdapter = this.getExtraAdapter();
        if (!extraAdapter)
            return;
        extraAdapter.putExtra(extraName, value);
    };
    AbstractType.prototype.getExtra = function (name) {
        var extraAdapter = this.getExtraAdapter();
        if (!extraAdapter)
            return null;
        return extraAdapter.getExtra(name);
    };
    AbstractType.prototype.getExtraAdapter = function () {
        if (this.getAdapters()) {
            var extraAdapter = _.find(this.getAdapters(), function (adapter) {
                //weird duck-typing, but we can touch anything from nominal-types here
                if (adapter.getExtra && typeof (adapter.getExtra) == "function"
                    && adapter.putExtra && typeof (adapter.putExtra) == "function") {
                    return true;
                }
            });
            return extraAdapter;
        }
        return null;
    };
    return AbstractType;
}(Described));
exports.AbstractType = AbstractType;
var ValueType = /** @class */ (function (_super) {
    __extends(ValueType, _super);
    function ValueType(name, _universe, path, description) {
        if (_universe === void 0) { _universe = null; }
        if (path === void 0) { path = ""; }
        if (description === void 0) { description = ""; }
        return _super.call(this, name, _universe, path) || this;
    }
    ValueType.prototype.hasStructure = function () {
        return false;
    };
    ValueType.prototype.hasValueTypeInHierarchy = function () {
        return true;
    };
    ValueType.prototype.isValueType = function () {
        return true;
    };
    ValueType.prototype.isUnionType = function () {
        return false;
    };
    ValueType.prototype.isObject = function () {
        return false;
    };
    return ValueType;
}(AbstractType));
exports.ValueType = ValueType;
var StructuredType = /** @class */ (function (_super) {
    __extends(StructuredType, _super);
    function StructuredType() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this._properties = [];
        return _this;
    }
    StructuredType.prototype.hasStructure = function () {
        return true;
    };
    StructuredType.prototype.propertyIndex = function (name) {
        var props = this.properties();
        for (var i = 0; i < props.length; i++) {
            if (props[i].nameId() == name)
                return i;
        }
        return -1;
    };
    StructuredType.prototype.addProperty = function (name, range) {
        return new Property(name).withDomain(this).withRange(range);
    };
    StructuredType.prototype.allPropertyIndex = function (name) {
        var props = this.allProperties();
        for (var i = 0; i < props.length; i++) {
            if (props[i].nameId() == name)
                return i;
        }
        return -1;
    };
    StructuredType.prototype.properties = function () {
        return [].concat(this._properties);
    };
    StructuredType.prototype.registerProperty = function (p) {
        if (p.domain() != this) {
            throw new Error(messageRegistry.SHOULD_BE_ALREADY_OWNED.message);
        }
        if (this._properties.indexOf(p) != -1) {
            throw new Error(messageRegistry.ALREADY_INCLUDED.message);
        }
        this._properties.push(p);
    };
    return StructuredType;
}(AbstractType));
exports.StructuredType = StructuredType;
var Property = /** @class */ (function (_super) {
    __extends(Property, _super);
    function Property() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this._keyShouldStartFrom = null;
        _this._isRequired = false;
        _this._isMultiValue = false;
        _this._descriminates = false;
        _this._defaultBooleanValue = null;
        _this._defaultIntegerValue = null;
        return _this;
    }
    Property.isInstance = function (instance) {
        if (instance != null && instance.getClassIdentifier
            && typeof (instance.getClassIdentifier) == "function") {
            for (var _i = 0, _a = instance.getClassIdentifier(); _i < _a.length; _i++) {
                var currentIdentifier = _a[_i];
                if (currentIdentifier == Property.CLASS_IDENTIFIER_Property)
                    return true;
            }
        }
        return false;
    };
    Property.prototype.getClassIdentifier = function () {
        var superIdentifiers = _super.prototype.getClassIdentifier.call(this);
        return superIdentifiers.concat(Property.CLASS_IDENTIFIER_Property);
    };
    Property.prototype.withMultiValue = function (v) {
        if (v === void 0) { v = true; }
        this._isMultiValue = v;
        return this;
    };
    Property.prototype.withDescriminating = function (b) {
        this._descriminates = b;
        return this;
    };
    Property.prototype.withRequired = function (req) {
        this._isRequired = req;
        return this;
    };
    Property.prototype.isRequired = function () {
        return this._isRequired;
    };
    Property.prototype.withKeyRestriction = function (keyShouldStartFrom) {
        this._keyShouldStartFrom = keyShouldStartFrom;
        return this;
    };
    Property.prototype.withDomain = function (d, custom) {
        if (custom === void 0) { custom = false; }
        this._ownerClass = d;
        if (custom) {
            d.registerCustomProperty(this);
        }
        else {
            d.registerProperty(this);
        }
        return this;
    };
    Property.prototype.setDefaultVal = function (s) {
        this._defaultValue = s;
        return this;
    };
    Property.prototype.setDefaultBooleanVal = function (s) {
        this._defaultBooleanValue = s;
        return this;
    };
    Property.prototype.setDefaultIntegerVal = function (s) {
        this._defaultIntegerValue = s;
        return this;
    };
    Property.prototype.defaultValue = function () {
        if (this._defaultValue != null) {
            return this._defaultValue;
        }
        else if (this._defaultBooleanValue != null) {
            return this._defaultBooleanValue;
        }
        else if (this._defaultIntegerValue != null) {
            return this._defaultIntegerValue;
        }
        return null;
    };
    Property.prototype.isPrimitive = function () {
        return false;
    };
    Property.prototype.withRange = function (t) {
        this._nodeRange = t;
        return this;
    };
    Property.prototype.isValueProperty = function () {
        return this._nodeRange.hasValueTypeInHierarchy();
    };
    Property.prototype.enumOptions = function () {
        if (this._enumOptions && typeof this._enumOptions == 'string') {
            return [this._enumOptions + ""];
        }
        return this._enumOptions;
    };
    Property.prototype.keyPrefix = function () {
        return this._keyShouldStartFrom;
    };
    Property.prototype.withEnumOptions = function (op) {
        this._enumOptions = op;
        return this;
    };
    Property.prototype.withKeyRegexp = function (regexp) {
        this._keyRegexp = regexp;
        return this;
    };
    Property.prototype.getKeyRegexp = function () {
        return this._keyRegexp;
    };
    Property.prototype.matchKey = function (k) {
        if (k == null) {
            return false;
        }
        if (this._groupName != null) {
            return this._groupName == k;
        }
        else {
            if (this._keyShouldStartFrom != null) {
                if (k.indexOf(this._keyShouldStartFrom) == 0) {
                    return true;
                }
            }
            if (this._enumOptions) {
                if (this._enumOptions.indexOf(k) != -1) {
                    return true;
                }
            }
            if (this.getKeyRegexp()) {
                try {
                    if (new RegExp(this.getKeyRegexp()).test(k)) {
                        return true;
                    }
                }
                catch (Error) {
                    //ignoring as the most probable reason for an error is an invalid pattern, we dont want to spam log
                    //with that kind of exceptions
                }
            }
            return false;
        }
    };
    Property.prototype.getFacetValidator = function () {
        return this.facetValidator;
    };
    Property.prototype.setFacetValidator = function (f) {
        this.facetValidator = f;
    };
    Property.prototype.domain = function () {
        return this._ownerClass;
    };
    Property.prototype.range = function () {
        return this._nodeRange;
    };
    Property.prototype.isMultiValue = function () {
        if (this.range() && this.range().hasArrayInHierarchy()) {
            return true;
        }
        return this._isMultiValue;
    };
    Property.prototype.isDescriminator = function () {
        return this._descriminates;
    };
    Property.CLASS_IDENTIFIER_Property = "nominal-types.Property";
    return Property;
}(Described));
exports.Property = Property;
var Union = /** @class */ (function (_super) {
    __extends(Union, _super);
    function Union() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Union.prototype.key = function () {
        return null;
    };
    Union.prototype.leftType = function () {
        return this.left;
    };
    Union.prototype.rightType = function () {
        return this.right;
    };
    Union.prototype.isUserDefined = function () {
        return true;
    };
    Union.prototype.unionInHierarchy = function () {
        return this;
    };
    Union.prototype.union = function () {
        return this;
    };
    Union.prototype.hasUnionInHierarchy = function () {
        return true;
    };
    Union.prototype.isUnion = function () {
        return true;
    };
    Union.prototype.isObject = function () {
        return this.leftType().isObject() && this.rightType().isObject();
    };
    Union.prototype.hasArrayInHierarchy = function () {
        if (this.left && this.right) {
            return this.left.hasArrayInHierarchy() || this.right.hasArrayInHierarchy();
        }
        if (this.left) {
            return this.left.hasArrayInHierarchy();
        }
        if (this.right) {
            return this.right.hasArrayInHierarchy();
        }
    };
    return Union;
}(AbstractType));
exports.Union = Union;
var Array = /** @class */ (function (_super) {
    __extends(Array, _super);
    function Array() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Array.prototype.hasArrayInHierarchy = function () {
        return true;
    };
    Array.prototype.isArray = function () {
        return true;
    };
    Array.prototype.isObject = function () {
        return false;
    };
    Array.prototype.arrayInHierarchy = function () {
        return this;
    };
    Array.prototype.array = function () {
        return this;
    };
    Array.prototype.isUserDefined = function () {
        return true;
    };
    Array.prototype.componentType = function () {
        return this.component;
    };
    Array.prototype.setComponent = function (t) {
        this.component = t;
    };
    Array.prototype.key = function () {
        return null;
    };
    return Array;
}(AbstractType));
exports.Array = Array;
var ExternalType = /** @class */ (function (_super) {
    __extends(ExternalType, _super);
    function ExternalType() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    ExternalType.prototype.externalInHierarchy = function () {
        return this;
    };
    ExternalType.prototype.typeId = function () {
        return this.schemaString;
    };
    ExternalType.prototype.schema = function () {
        return this.schemaString;
    };
    ExternalType.prototype.isUserDefined = function () {
        return true;
    };
    ExternalType.prototype.hasExternalInHierarchy = function () {
        return true;
    };
    ExternalType.prototype.isExternal = function () {
        return true;
    };
    ExternalType.prototype.external = function () {
        return this;
    };
    return ExternalType;
}(StructuredType));
exports.ExternalType = ExternalType;
function getUniqueTypeId(type) {
    var uniqueTypeId = type.typeId();
    if (!uniqueTypeId) {
        var adapter = type.getAdapter(typesystem_1.InheritedType);
        uniqueTypeId = (adapter && (adapter.id() + '')) || '';
    }
    return uniqueTypeId;
}
//# sourceMappingURL=nominal-types.js.map