text: import { __read, __spreadArray } from "tslib";
  import { EntityColumnNotFound } from "..";
  import { CannotCreateEntityIdMapError } from "../error/CannotCreateEntityIdMapError";
  import { OrmUtils } from "../util/OrmUtils";
  import { shorten } from "../util/StringUtils";
  /**
   * Contains all entity metadata.
   */
  var EntityMetadata = /** @class */ (function () {
      // ---------------------------------------------------------------------
      // Constructor
      // ---------------------------------------------------------------------
      function EntityMetadata(options) {
          /**
           * Children entity metadatas. Used in inheritance patterns.
           */
          this.childEntityMetadatas = [];
          /**
           * All "inheritance tree" from a target entity.
           * For example for target Post < ContentModel < Unit it will be an array of [Post, ContentModel, Unit].
           * It also contains child entities for single table inheritance.
           */
          this.inheritanceTree = [];
          /**
           * Table type. Tables can be closure, junction, etc.
           */
          this.tableType = "regular";
          /**
           * Enables Sqlite "WITHOUT ROWID" modifier for the "CREATE TABLE" statement
           */
          this.withoutRowid = false;
          /**
           * Indicates if schema will be synchronized for this entity or not.
           */
          this.synchronize = true;
          /**
           * Checks if there any non-nullable column exist in this entity.
           */
          this.hasNonNullableRelations = false;
          /**
           * Indicates if this entity metadata of a junction table, or not.
           * Junction table is a table created by many-to-many relationship.
           *
           * Its also possible to understand if entity is junction via tableType.
           */
          this.isJunction = false;
          /**
           * Indicates if the entity should be instantiated using the constructor
           * or via allocating a new object via `Object.create()`.
           */
          this.isAlwaysUsingConstructor = true;
          /**
           * Checks if this table is a junction table of the closure table.
           * This type is for tables that contain junction metadata of the closure tables.
           */
          this.isClosureJunction = false;
          /**
           * Checks if entity's table has multiple primary columns.
           */
          this.hasMultiplePrimaryKeys = false;
          /**
           * Indicates if this entity metadata has uuid generated columns.
           */
          this.hasUUIDGeneratedColumns = false;
          /**
           * Entity's column metadatas defined by user.
           */
          this.ownColumns = [];
          /**
           * Columns of the entity, including columns that are coming from the embeddeds of this entity.
           */
          this.columns = [];
          /**
           * Ancestor columns used only in closure junction tables.
           */
          this.ancestorColumns = [];
          /**
           * Descendant columns used only in closure junction tables.
           */
          this.descendantColumns = [];
          /**
           * All columns except for virtual columns.
           */
          this.nonVirtualColumns = [];
          /**
           * In the case if this entity metadata is junction table's entity metadata,
           * this will contain all referenced columns of owner entity.
           */
          this.ownerColumns = [];
          /**
           * In the case if this entity metadata is junction table's entity metadata,
           * this will contain all referenced columns of inverse entity.
           */
          this.inverseColumns = [];
          /**
           * Gets the column with generated flag.
           */
          this.generatedColumns = [];
          /**
           * Gets the primary columns.
           */
          this.primaryColumns = [];
          /**
           * Entity's relation metadatas.
           */
          this.ownRelations = [];
          /**
           * Relations of the entity, including relations that are coming from the embeddeds of this entity.
           */
          this.relations = [];
          /**
           * List of eager relations this metadata has.
           */
          this.eagerRelations = [];
          /**
           * List of eager relations this metadata has.
           */
          this.lazyRelations = [];
          /**
           * Gets only one-to-one relations of the entity.
           */
          this.oneToOneRelations = [];
          /**
           * Gets only owner one-to-one relations of the entity.
           */
          this.ownerOneToOneRelations = [];
          /**
           * Gets only one-to-many relations of the entity.
           */
          this.oneToManyRelations = [];
          /**
           * Gets only many-to-one relations of the entity.
           */
          this.manyToOneRelations = [];
          /**
           * Gets only many-to-many relations of the entity.
           */
          this.manyToManyRelations = [];
          /**
           * Gets only owner many-to-many relations of the entity.
           */
          this.ownerManyToManyRelations = [];
          /**
           * Gets only owner one-to-one and many-to-one relations.
           */
          this.relationsWithJoinColumns = [];
          /**
           * Entity's relation id metadatas.
           */
          this.relationIds = [];
          /**
           * Entity's relation id metadatas.
           */
          this.relationCounts = [];
          /**
           * Entity's foreign key metadatas.
           */
          this.foreignKeys = [];
          /**
           * Entity's embedded metadatas.
           */
          this.embeddeds = [];
          /**
           * All embeddeds - embeddeds from this entity metadata and from all child embeddeds, etc.
           */
          this.allEmbeddeds = [];
          /**
           * Entity's own indices.
           */
          this.ownIndices = [];
          /**
           * Entity's index metadatas.
           */
          this.indices = [];
          /**
           * Entity's unique metadatas.
           */
          this.uniques = [];
          /**
           * Entity's own uniques.
           */
          this.ownUniques = [];
          /**
           * Entity's check metadatas.
           */
          this.checks = [];
          /**
           * Entity's exclusion metadatas.
           */
          this.exclusions = [];
          /**
           * Entity's own listener metadatas.
           */
          this.ownListeners = [];
          /**
           * Entity listener metadatas.
           */
          this.listeners = [];
          /**
           * Listener metadatas with "AFTER LOAD" type.
           */
          this.afterLoadListeners = [];
          /**
           * Listener metadatas with "AFTER INSERT" type.
           */
          this.beforeInsertListeners = [];
          /**
           * Listener metadatas with "AFTER INSERT" type.
           */
          this.afterInsertListeners = [];
          /**
           * Listener metadatas with "AFTER UPDATE" type.
           */
          this.beforeUpdateListeners = [];
          /**
           * Listener metadatas with "AFTER UPDATE" type.
           */
          this.afterUpdateListeners = [];
          /**
           * Listener metadatas with "AFTER REMOVE" type.
           */
          this.beforeRemoveListeners = [];
          /**
           * Listener metadatas with "BEFORE SOFT REMOVE" type.
           */
          this.beforeSoftRemoveListeners = [];
          /**
           * Listener metadatas with "BEFORE RECOVER" type.
           */
          this.beforeRecoverListeners = [];
          /**
           * Listener metadatas with "AFTER REMOVE" type.
           */
          this.afterRemoveListeners = [];
          /**
           * Listener metadatas with "AFTER SOFT REMOVE" type.
           */
          this.afterSoftRemoveListeners = [];
          /**
           * Listener metadatas with "AFTER RECOVER" type.
           */
          this.afterRecoverListeners = [];
          this.connection = options.connection;
          this.inheritanceTree = options.inheritanceTree || [];
          this.inheritancePattern = options.inheritancePattern;
          this.treeType = options.tableTree ? options.tableTree.type : undefined;
          this.treeOptions = options.tableTree ? options.tableTree.options : undefined;
          this.parentClosureEntityMetadata = options.parentClosureEntityMetadata;
          this.tableMetadataArgs = options.args;
          this.target = this.tableMetadataArgs.target;
          this.tableType = this.tableMetadataArgs.type;
          this.expression = this.tableMetadataArgs.expression;
          this.withoutRowid = this.tableMetadataArgs.withoutRowid;
          this.dependsOn = this.tableMetadataArgs.dependsOn;
      }
      // -------------------------------------------------------------------------
      // Public Methods
      // -------------------------------------------------------------------------
      /**
       * Creates a new entity.
       */
      EntityMetadata.prototype.create = function (queryRunner, options) {
          var _this = this;
          // if target is set to a function (e.g. class) that can be created then create it
          var ret;
          if (this.target instanceof Function) {
              if (!(options === null || options === void 0 ? void 0 : options.fromDeserializer) || this.isAlwaysUsingConstructor) {
                  ret = new this.target();
              }
              else {
                  ret = Object.create(this.target.prototype);
              }
              this.lazyRelations.forEach(function (relation) { return _this.connection.relationLoader.enableLazyLoad(relation, ret, queryRunner); });
              return ret;
          }
          // otherwise simply return a new empty object
          var newObject = {};
          this.lazyRelations.forEach(function (relation) { return _this.connection.relationLoader.enableLazyLoad(relation, newObject, queryRunner); });
          return newObject;
      };
      /**
       * Checks if given entity has an id.
       */
      EntityMetadata.prototype.hasId = function (entity) {
          if (!entity)
              return false;
          return this.primaryColumns.every(function (primaryColumn) {
              var value = primaryColumn.getEntityValue(entity);
              return value !== null && value !== undefined && value !== "";
          });
      };
      /**
       * Checks if given entity / object contains ALL primary keys entity must have.
       * Returns true if it contains all of them, false if at least one of them is not defined.
       */
      EntityMetadata.prototype.hasAllPrimaryKeys = function (entity) {
          return this.primaryColumns.every(function (primaryColumn) {
              var value = primaryColumn.getEntityValue(entity);
              return value !== null && value !== undefined;
          });
      };
      /**
       * Ensures that given object is an entity id map.
       * If given id is an object then it means its already id map.
       * If given id isn't an object then it means its a value of the id column
       * and it creates a new id map with this value and name of the primary column.
       */
      EntityMetadata.prototype.ensureEntityIdMap = function (id) {
          if (id instanceof Object)
              return id;
          if (this.hasMultiplePrimaryKeys)
              throw new CannotCreateEntityIdMapError(this, id);
          return this.primaryColumns[0].createValueMap(id);
      };
      /**
       * Gets primary keys of the entity and returns them in a literal object.
       * For example, for Post{ id: 1, title: "hello" } where id is primary it will return { id: 1 }
       * For multiple primary keys it returns multiple keys in object.
       * For primary keys inside embeds it returns complex object literal with keys in them.
       */
      EntityMetadata.prototype.getEntityIdMap = function (entity) {
          if (!entity)
              return undefined;
          return EntityMetadata.getValueMap(entity, this.primaryColumns, { skipNulls: true });
      };
      /**
       * Creates a "mixed id map".
       * If entity has multiple primary keys (ids) then it will return just regular id map, like what getEntityIdMap returns.
       * But if entity has a single primary key then it will return just value of the id column of the entity, just value.
       * This is called mixed id map.
       */
      EntityMetadata.prototype.getEntityIdMixedMap = function (entity) {
          if (!entity)
              return entity;
          var idMap = this.getEntityIdMap(entity);
          if (this.hasMultiplePrimaryKeys) {
              return idMap;
          }
          else if (idMap) {
              return this.primaryColumns[0].getEntityValue(idMap); // todo: what about parent primary column?
          }
          return idMap;
      };
      /**
       * Compares two different entities by their ids.
       * Returns true if they match, false otherwise.
       */
      EntityMetadata.prototype.compareEntities = function (firstEntity, secondEntity) {
          var firstEntityIdMap = this.getEntityIdMap(firstEntity);
          if (!firstEntityIdMap)
              return false;
          var secondEntityIdMap = this.getEntityIdMap(secondEntity);
          if (!secondEntityIdMap)
              return false;
          return OrmUtils.compareIds(firstEntityIdMap, secondEntityIdMap);
      };
      /**
       * Finds column with a given property name.
       */
      EntityMetadata.prototype.findColumnWithPropertyName = function (propertyName) {
          return this.columns.find(function (column) { return column.propertyName === propertyName; });
      };
      /**
       * Finds column with a given database name.
       */
      EntityMetadata.prototype.findColumnWithDatabaseName = function (databaseName) {
          return this.columns.find(function (column) { return column.databaseName === databaseName; });
      };
      /**
       * Checks if there is a column or relationship with a given property path.
       */
      EntityMetadata.prototype.hasColumnWithPropertyPath = function (propertyPath) {
          var hasColumn = this.columns.some(function (column) { return column.propertyPath === propertyPath; });
          return hasColumn || this.hasRelationWithPropertyPath(propertyPath);
      };
      /**
       * Finds column with a given property path.
       */
      EntityMetadata.prototype.findColumnWithPropertyPath = function (propertyPath) {
          var column = this.columns.find(function (column) { return column.propertyPath === propertyPath; });
          if (column)
              return column;
          // in the case if column with property path was not found, try to find a relation with such property path
          // if we find relation and it has a single join column then its the column user was seeking
          var relation = this.relations.find(function (relation) { return relation.propertyPath === propertyPath; });
          if (relation && relation.joinColumns.length === 1)
              return relation.joinColumns[0];
          return undefined;
      };
      /**
       * Finds columns with a given property path.
       * Property path can match a relation, and relations can contain multiple columns.
       */
      EntityMetadata.prototype.findColumnsWithPropertyPath = function (propertyPath) {
          var column = this.columns.find(function (column) { return column.propertyPath === propertyPath; });
          if (column)
              return [column];
          // in the case if column with property path was not found, try to find a relation with such property path
          // if we find relation and it has a single join column then its the column user was seeking
          var relation = this.findRelationWithPropertyPath(propertyPath);
          if (relation && relation.joinColumns)
              return relation.joinColumns;
          return [];
      };
      /**
       * Checks if there is a relation with the given property path.
       */
      EntityMetadata.prototype.hasRelationWithPropertyPath = function (propertyPath) {
          return this.relations.some(function (relation) { return relation.propertyPath === propertyPath; });
      };
      /**
       * Finds relation with the given property path.
       */
      EntityMetadata.prototype.findRelationWithPropertyPath = function (propertyPath) {
          return this.relations.find(function (relation) { return relation.propertyPath === propertyPath; });
      };
      /**
       * Checks if there is an embedded with a given property path.
       */
      EntityMetadata.prototype.hasEmbeddedWithPropertyPath = function (propertyPath) {
          return this.allEmbeddeds.some(function (embedded) { return embedded.propertyPath === propertyPath; });
      };
      /**
       * Finds embedded with a given property path.
       */
      EntityMetadata.prototype.findEmbeddedWithPropertyPath = function (propertyPath) {
          return this.allEmbeddeds.find(function (embedded) { return embedded.propertyPath === propertyPath; });
      };
      /**
       * Returns an array of databaseNames mapped from provided propertyPaths
       */
      EntityMetadata.prototype.mapPropertyPathsToColumns = function (propertyPaths) {
          var _this = this;
          return propertyPaths.map(function (propertyPath) {
              var column = _this.findColumnWithPropertyPath(propertyPath);
              if (column == null) {
                  throw new EntityColumnNotFound(propertyPath);
              }
              return column;
          });
      };
      /**
       * Iterates through entity and finds and extracts all values from relations in the entity.
       * If relation value is an array its being flattened.
       */
      EntityMetadata.prototype.extractRelationValuesFromEntity = function (entity, relations) {
          var _this = this;
          var relationsAndValues = [];
          relations.forEach(function (relation) {
              var value = relation.getEntityValue(entity);
              if (Array.isArray(value)) {
                  value.forEach(function (subValue) { return relationsAndValues.push([relation, subValue, _this.getInverseEntityMetadata(subValue, relation)]); });
              }
              else if (value) {
                  relationsAndValues.push([relation, value, _this.getInverseEntityMetadata(value, relation)]);
              }
          });
          return relationsAndValues;
      };
      EntityMetadata.prototype.getInverseEntityMetadata = function (value, relation) {
          var childEntityMetadata = relation.inverseEntityMetadata.childEntityMetadatas.find(function (metadata) {
              return metadata.target === value.constructor;
          });
          return childEntityMetadata ? childEntityMetadata : relation.inverseEntityMetadata;
      };
      // -------------------------------------------------------------------------
      // Public Static Methods
      // -------------------------------------------------------------------------
      /**
       * Creates a property paths for a given entity.
       *
       * @deprecated
       */
      EntityMetadata.createPropertyPath = function (metadata, entity, prefix) {
          var _this = this;
          if (prefix === void 0) { prefix = ""; }
          var paths = [];
          Object.keys(entity).forEach(function (key) {
              // check for function is needed in the cases when createPropertyPath used on values containg a function as a value
              // example: .update().set({ name: () => `SUBSTR('', 1, 2)` })
              var parentPath = prefix ? prefix + "." + key : key;
              if (metadata.hasEmbeddedWithPropertyPath(parentPath)) {
                  var subPaths = _this.createPropertyPath(metadata, entity[key], parentPath);
                  paths.push.apply(paths, __spreadArray([], __read(subPaths), false));
              }
              else {
                  var path = prefix ? prefix + "." + key : key;
                  paths.push(path);
              }
          });
          return paths;
      };
      /**
       * Finds difference between two entity id maps.
       * Returns items that exist in the first array and absent in the second array.
       */
      EntityMetadata.difference = function (firstIdMaps, secondIdMaps) {
          return firstIdMaps.filter(function (firstIdMap) {
              return !secondIdMaps.find(function (secondIdMap) { return OrmUtils.compareIds(firstIdMap, secondIdMap); });
          });
      };
      /**
       * Creates value map from the given values and columns.
       * Examples of usages are primary columns map and join columns map.
       */
      EntityMetadata.getValueMap = function (entity, columns, options) {
          return columns.reduce(function (map, column) {
              var value = column.getEntityValueMap(entity, options);
              // make sure that none of the values of the columns are not missing
              if (map === undefined || value === null || value === undefined)
                  return undefined;
              return OrmUtils.mergeDeep(map, value);
          }, {});
      };
      // ---------------------------------------------------------------------
      // Public Builder Methods
      // ---------------------------------------------------------------------
      EntityMetadata.prototype.build = function () {
          var _a;
          var namingStrategy = this.connection.namingStrategy;
          var entityPrefix = this.connection.options.entityPrefix;
          var entitySkipConstructor = this.connection.options.entitySkipConstructor;
          this.engine = this.tableMetadataArgs.engine;
          this.database = this.tableMetadataArgs.type === "entity-child" && this.parentEntityMetadata ? this.parentEntityMetadata.database : this.tableMetadataArgs.database;
          if (this.tableMetadataArgs.schema) {
              this.schema = this.tableMetadataArgs.schema;
          }
          else if ((this.tableMetadataArgs.type === "entity-child") && this.parentEntityMetadata) {
              this.schema = this.parentEntityMetadata.schema;
          }
          else if ((_a = this.connection.options) === null || _a === void 0 ? void 0 : _a.hasOwnProperty("schema")) {
              this.schema = this.connection.options.schema;
          }
          this.givenTableName = this.tableMetadataArgs.type === "entity-child" && this.parentEntityMetadata ? this.parentEntityMetadata.givenTableName : this.tableMetadataArgs.name;
          this.synchronize = this.tableMetadataArgs.synchronize === false ? false : true;
          this.targetName = this.tableMetadataArgs.target instanceof Function ? this.tableMetadataArgs.target.name : this.tableMetadataArgs.target;
          if (this.tableMetadataArgs.type === "closure-junction") {
              this.tableNameWithoutPrefix = namingStrategy.closureJunctionTableName(this.givenTableName);
          }
          else if (this.tableMetadataArgs.type === "entity-child" && this.parentEntityMetadata) {
              this.tableNameWithoutPrefix = namingStrategy.tableName(this.parentEntityMetadata.targetName, this.parentEntityMetadata.givenTableName);
          }
          else {
              this.tableNameWithoutPrefix = namingStrategy.tableName(this.targetName, this.givenTableName);
              if (this.connection.driver.maxAliasLength && this.connection.driver.maxAliasLength > 0 && this.tableNameWithoutPrefix.length > this.connection.driver.maxAliasLength) {
                  this.tableNameWithoutPrefix = shorten(this.tableNameWithoutPrefix, { separator: "_", segmentLength: 3 });
              }
          }
          this.tableName = entityPrefix ? namingStrategy.prefixTableName(entityPrefix, this.tableNameWithoutPrefix) : this.tableNameWithoutPrefix;
          this.target = this.target ? this.target : this.tableName;
          this.name = this.targetName ? this.targetName : this.tableName;
          this.expression = this.tableMetadataArgs.expression;
          this.withoutRowid = this.tableMetadataArgs.withoutRowid === true ? true : false;
          this.tablePath = this.connection.driver.buildTableName(this.tableName, this.schema, this.database);
          this.orderBy = (this.tableMetadataArgs.orderBy instanceof Function) ? this.tableMetadataArgs.orderBy(this.propertiesMap) : this.tableMetadataArgs.orderBy; // todo: is propertiesMap available here? Looks like its not
          if (entitySkipConstructor !== undefined) {
              this.isAlwaysUsingConstructor = !entitySkipConstructor;
          }
          this.isJunction = this.tableMetadataArgs.type === "closure-junction" || this.tableMetadataArgs.type === "junction";
          this.isClosureJunction = this.tableMetadataArgs.type === "closure-junction";
      };
      /**
       * Registers a new column in the entity and recomputes all depend properties.
       */
      EntityMetadata.prototype.registerColumn = function (column) {
          if (this.ownColumns.indexOf(column) !== -1)
              return;
          this.ownColumns.push(column);
          this.columns = this.embeddeds.reduce(function (columns, embedded) { return columns.concat(embedded.columnsFromTree); }, this.ownColumns);
          this.primaryColumns = this.columns.filter(function (column) { return column.isPrimary; });
          this.hasMultiplePrimaryKeys = this.primaryColumns.length > 1;
          this.hasUUIDGeneratedColumns = this.columns.filter(function (column) { return column.isGenerated || column.generationStrategy === "uuid"; }).length > 0;
          this.propertiesMap = this.createPropertiesMap();
          if (this.childEntityMetadatas)
              this.childEntityMetadatas.forEach(function (entityMetadata) { return entityMetadata.registerColumn(column); });
      };
      /**
       * Creates a special object - all columns and relations of the object (plus columns and relations from embeds)
       * in a special format - { propertyName: propertyName }.
       *
       * example: Post{ id: number, name: string, counterEmbed: { count: number }, category: Category }.
       * This method will create following object:
       * { id: "id", counterEmbed: { count: "counterEmbed.count" }, category: "category" }
       */
      EntityMetadata.prototype.createPropertiesMap = function () {
          var map = {};
          this.columns.forEach(function (column) { return OrmUtils.mergeDeep(map, column.createValueMap(column.propertyPath)); });
          this.relations.forEach(function (relation) { return OrmUtils.mergeDeep(map, relation.createValueMap(relation.propertyPath)); });
          return map;
      };
      return EntityMetadata;
  }());
  export { EntityMetadata };
  
  //# sourceMappingURL=EntityMetadata.js.map
  