All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.model.impl.EntityImpl Maven / Gradle / Ivy

There is a newer version: 21.16.1.0
Show newest version
/*
 * Tentackle - http://www.tentackle.org.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.tentackle.model.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import org.tentackle.common.BasicStringHelper;
import org.tentackle.common.Compare;
import org.tentackle.common.Constants;
import org.tentackle.model.Attribute;
import org.tentackle.model.AttributeSorting;
import org.tentackle.model.DataType;
import org.tentackle.model.Entity;
import org.tentackle.model.Index;
import org.tentackle.model.InheritanceType;
import org.tentackle.model.Integrity;
import org.tentackle.model.ModelException;
import org.tentackle.model.Relation;
import org.tentackle.model.SourceInfo;
import org.tentackle.model.parser.ConfigurationLine;
import org.tentackle.sql.Backend;

/**
 * The entity model implementation.
 *
 * @author harald
 */
public class EntityImpl implements Entity, Comparable {

  /** name = ... */
  static final String NAME = "NAME";

  /** id = ... */
  static final String ID = "ID";

  /** table = ... */
  static final String TABLE = "TABLE";

  /** alias = ... */
  static final String ALIAS = "ALIAS";

  /** integrity = ... */
  static final String INTEGRITY = "INTEGRITY";

  /** inheritance = ... */
  static final String INHERITANCE = "INHERITANCE";

  /** extends = ... */
  static final String EXTENDS = "EXTENDS";

  /** comment = ... */
  static final String COMMENT = "COMMENT";


  private final EntityFactoryImpl factory;                  // the factory
  private final SourceInfo sourceInfo;                      // the source info

  private String name;                                      // the entities name
  private int classId;                                      // the class id
  private String tableName;                                 // the table name
  private String schemaName;                                // the schema name
  private String tableNameWithoutSchema;                    // schema relative table name
  private String tableAlias;                                // the table alias
  private Integrity integrity;                              // referential integrity
  private InheritanceType inheritanceType;                  // the inheritance type
  private String superEntityName;                           // the name of the extended super class
  private Entity superEntity;                               // the super entity
  private int inheritanceLevel;                             // number of super entities
  private final List subEntities;                   // the sub entities
  private final EntityOptionsImpl options;                  // options
  private final List attributes;                 // the attributes
  private Attribute contextIdAttribute;                     // attribute holding the context id, null if none
  private List uniqueDomainKey;                  // the unique domain key attributes
  private List sorting;                   // the default sorting
  private final List relations;                   // the relations
  private final List referencingRelations;        // the referencing relations from other entities
  private final List indexes;                        // the indexes
  private final Set> compositePaths;         // paths of composite relations to this entity
  private final List deepReferencesToComponents;  // deep references to components of this root entity
  private final List deepReferences;              // deep references to this entity
  private boolean deeplyReferenced;                         // true if this or any component is deeply referenced
  private boolean modelRoot;                                // is root entity according to model
  private boolean modelRootId;                              // provides a root-ID according to model
  private boolean modelRootClassId;                         // provides a root-class-ID according to model



  /**
   * Creates an entity.
   *
   * @param factory the factory to create entity options
   * @param sourceInfo the source information
   */
  public EntityImpl(EntityFactoryImpl factory, SourceInfo sourceInfo) {
    this.factory = factory;
    this.sourceInfo = sourceInfo;

    integrity = Integrity.NONE;
    inheritanceType = InheritanceType.NONE;
    options = factory.createEntityOptions(this, sourceInfo);
    attributes = new ArrayList<>();
    relations = new ArrayList<>();
    referencingRelations = new ArrayList<>();
    subEntities = new ArrayList<>();
    compositePaths = new HashSet<>();
    deepReferences = new ArrayList<>();
    deepReferencesToComponents = new ArrayList<>();
    indexes = new ArrayList<>();
  }


  @Override
  public int hashCode() {
    int hash = 7;
    hash = 17 * hash + Objects.hashCode(this.name);
    return hash;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == null) {
      return false;
    }
    if (getClass() != obj.getClass()) {
      return false;
    }
    final EntityImpl other = (EntityImpl) obj;
    return Objects.equals(this.name, other.name);
  }

  @Override
  public int compareTo(EntityImpl o) {
    return Compare.compare(name, o.name);
  }

  /**
   * Parses a configuration line.
   *
   * @param line the configuation line
   * @return true if known configuration processed
   * @throws ModelException if parsing failed
   */
  public boolean parseConfiguration(ConfigurationLine line) throws ModelException {

    boolean parsed = true;

    switch (line.getKey().toUpperCase()) {

      case NAME:
        String str = line.getValue();
        if (str.indexOf('.') >= 0) {
          throw line.createModelException("entity name must not contain package name");
        }
        if (!BasicStringHelper.isValidJavaClassName(str)) {
          throw line.createModelException("'" + str + "' is not a valid Java class name");
        }
        setName(str);
        break;

      case ID:
        String idStr = line.getValue();
        setClassId(Integer.parseInt(idStr));
        break;

      case TABLE:
        setTableName(line.getValue());
        break;

      case ALIAS:
        setTableAlias(line.getValue());
        break;

      case INTEGRITY:
        try {
          setIntegrity(Integrity.valueOf(line.getValue().toUpperCase()));
        }
        catch (IllegalArgumentException ex) {
          throw line.createModelException("illegal integrity level: " + line.getValue());
        }
        break;

      case INHERITANCE:
        try {
          setInheritanceType(InheritanceType.valueOf(line.getValue().toUpperCase()));
        }
        catch (IllegalArgumentException ex) {
          throw line.createModelException("illegal inhertitance type: " + line.getValue());
        }
        break;

      case EXTENDS:
        setSuperEntityName(line.getValue());
        break;

      case COMMENT:
        getOptions().setComment(line.getValue());
        break;

      default:
        parsed = false;
    }

    return parsed;
  }




  @Override
  public String getName() {
    return name;
  }

  /**
   * Sets the entity's name.
   *
   * @param name the name
   */
  public void setName(String name) {
    this.name = name;
  }

  @Override
  public int getClassId() {
    return classId;
  }

  /**
   * Sets the entity id.
   *
   * @param classId the id
   */
  public void setClassId(int classId) {
    this.classId = classId;
  }

  @Override
  public String getTableName() {
    return tableName;
  }

  @Override
  public String getSchemaName() {
    return schemaName;
  }

  @Override
  public String getTableNameWithoutSchema() {
    return tableNameWithoutSchema;
  }

  @Override
  public String getTableAlias() {
    return tableAlias;
  }

  /**
   * Sets the tablename.
   *
   * @param tableName the tablename
   */
  public void setTableName(String tableName) {
    if (tableName == null) {
      this.tableName = null;
      schemaName = null;
      tableNameWithoutSchema = null;
    }
    else  {
      this.tableName = tableName.toLowerCase();
      int ndx = tableName.indexOf('.');
      if (ndx > 0) {
        schemaName = tableName.substring(0, ndx);
        tableNameWithoutSchema = tableName.substring(ndx + 1);
      }
      else  {
        tableNameWithoutSchema = this.tableName;
      }
    }
  }

  /**
   * Sets the table alias.
   *
   * @param tableAlias the alias
   */
  public void setTableAlias(String tableAlias) {
    this.tableAlias = tableAlias;
  }

  @Override
  public Integrity getIntegrity() {
    return integrity;
  }

  @Override
  public InheritanceType getInheritanceType() {
    return inheritanceType;
  }

  @Override
  public InheritanceType getHierarchyInheritanceType() {
    return superEntity == null ? inheritanceType : superEntity.getInheritanceType();
  }

  @Override
  public String getSuperEntityName() {
    return superEntityName;
  }

  @Override
  public Entity getSuperEntity() {
    return superEntity;
  }

  @Override
  public List getSuperEntities() {
    List superEntities = new ArrayList<>();
    Entity entity = this;
    while (entity.getSuperEntity() != null) {
      superEntities.add(entity.getSuperEntity());
      entity = entity.getSuperEntity();
    }
    return superEntities;
  }

  @Override
  public Entity getTopSuperEntity() {
    Entity entity = this;
    while (entity.getSuperEntity() != null) {
      entity = entity.getSuperEntity();
    }
    return entity;
  }

  @Override
  public List getInheritanceChain(Entity childEntity) throws ModelException {
    List chain = new ArrayList<>();
    Entity entity = childEntity;
    while (entity != null) {
      chain.add(0, entity);
      if (entity == this) {
        break;
      }
      entity = entity.getSuperEntity();
    }
    if (chain.isEmpty() || chain.get(0) != this) {
      throw new ModelException(this + " is not a superclass of " + childEntity);
    }
    return chain;
  }

  @Override
  public int getInheritanceLevel() {
    return inheritanceLevel;
  }

  @Override
  public List getSubEntities() {
    return subEntities;
  }

  @Override
  public List getAllSubEntities() {
    List childs = new ArrayList<>();
    collectSubEntities(childs, this);
    return childs;
  }

  @Override
  public List getLeafEntities() {
    List leafs = new ArrayList<>();
    collectLeafEntities(leafs, this);
    return leafs;
  }

  /**
   * Recursivly collects the leaf childs.
   *
   * @param leafs the list if leafs
   * @param entity the current entity to analyze
   */
  protected void collectLeafEntities(List leafs, Entity entity) {
    if (entity.isAbstract()) {
      for (Entity child: entity.getSubEntities()) {
        collectLeafEntities(leafs, child);
      }
    }
    else  {
      leafs.add(entity);
    }
  }

  /**
   * Recursivly collects all sub entities.
   *
   * @param subEntities the list of collected sub entities
   * @param entity the current entity to analyze
   */
  protected void collectSubEntities(List subEntities, Entity entity) {
    for (Entity sub: entity.getSubEntities()) {
      subEntities.add(sub);
      collectSubEntities(subEntities, sub);
    }
  }

  @Override
  public Set getAssociatedEntities() {
    Set associates = new HashSet<>();
    collectAssociatedEntities(associates, this, new HashSet<>());
    return associates;
  }


  /**
   * Recursively collects the associated entities for a given entity.
   *
   * @param associates the set of associated entities so far
   * @param entity the current entity to analyze
   * @param processedEntities the already processed entities (to avoid loops)
   */
  protected void collectAssociatedEntities(Set associates, Entity entity, Set processedEntities) {
    if (!processedEntities.contains(entity)) {
      processedEntities.add(entity);
      associates.add(entity);
      if (superEntity != null) {
        associates.add(superEntity);
        collectAssociatedEntities(associates, superEntity, processedEntities);
      }
      associates.addAll(getSubEntities());
      for (Entity child: getSubEntities()) {
        collectAssociatedEntities(associates, child, processedEntities);
      }
      for (Relation relation: getRelations()) {
        Entity relatedEntity = relation.getEntity();
        if (relatedEntity != null) {    // model may be incomplete...
          associates.add(relatedEntity);
          collectAssociatedEntities(associates, relatedEntity, processedEntities);
        }
      }
      for (Relation relation: getReferencingRelations()) {
        Entity foreignEntity = relation.getForeignEntity();
        if (foreignEntity != null) {    // model may be incomplete...
          associates.add(foreignEntity);
          collectAssociatedEntities(associates, foreignEntity, processedEntities);
        }
      }
    }
  }


  /**
   * Sets the integrity.
   *
   * @param integrity the integrity
   */
  public void setIntegrity(Integrity integrity) {
    if (integrity == null) {
      throw new IllegalArgumentException("integrity cannot be set to null");
    }
    this.integrity = integrity;
  }

  /**
   * Sets the inheritance type.
   *
   * @param inheritanceType the inheritance type
   */
  public void setInheritanceType(InheritanceType inheritanceType) {
    if (inheritanceType == null) {
      throw new IllegalArgumentException("inheritance type cannot be set to null");
    }
    this.inheritanceType = inheritanceType;
  }

  /**
   * Sets the inheritance level.
   *
   * @param inheritanceLevel the inheritance level
   */
  public void setInheritanceLevel(int inheritanceLevel) {
    this.inheritanceLevel = inheritanceLevel;
  }

  /**
   * Sets the name of the super entity.
   *
   * @param superEntityName the super entity
   */
  public void setSuperEntityName(String superEntityName) {
    this.superEntityName = superEntityName;
  }

  /**
   * Sets the super entity.
   *
   * @param superEntity the super entity
   */
  public void setSuperEntity(Entity superEntity) {
    this.superEntity = superEntity;
  }

  /**
   * Sets the context id attribute.
   *
   * @param contextIdAttribute the attribute providing the context ID
   */
  public void setContextIdAttribute(Attribute contextIdAttribute) {
    this.contextIdAttribute = contextIdAttribute;
  }

  /**
   * Sets the default sorting.
   *
   * @param sorting the sorting, null if none
   */
  public void setSorting(List sorting) {
    this.sorting = sorting;
  }

  @Override
  public EntityOptionsImpl getOptions() {
    return options;
  }

  @Override
  public List getAttributes() {
    return attributes;
  }


  /**
   * Returns whether this entity is a root-entity according to model.
   *
   * @return true if root entity
   */
  @Override
  public boolean isRootEntityAccordingToModel() {
    return modelRoot;
  }

  /**
   * Sets whether this entity is a root-entity according to model.
   *
   * @param modelRoot true if root entity
   */
  public void setRootEntityAccordingToModel(boolean modelRoot) {
    this.modelRoot = modelRoot;
  }

  /**
   * Returns whether this entity should provide a root class id according to model.
   *
   * @return true if should provide a root class id
   */
  @Override
  public boolean isProvidingRootClassIdAccordingToModel() {
    return modelRootClassId;
  }

  /**
   * Sets whether this entity should provide a root class id according to model.
   *
   * @param modelRootClassId  true if should provide a root class id
   */
  public void setProvidingRootClassIdAccordingToModel(boolean modelRootClassId) {
    this.modelRootClassId = modelRootClassId;
  }

  /**
   * Returns whether this entity should provide a root id according to model.
   *
   * @return true if should provide a root id
   */
  @Override
  public boolean isProvidingRootIdAccordingToModel() {
    return modelRootId;
  }

  /**
   * Sets whether this entity should provide a root id according to model.
   *
   * @param modelRootId  true if should provide a root id
   */
  public void setProvidingRootIdAccordingToModel(boolean modelRootId) {
    this.modelRootId = modelRootId;
  }


  @Override
  public List getInheritedAttributes() {
    List inheritedAttributes = new ArrayList<>();
    Entity se = getSuperEntity();
    while (se != null) {
      inheritedAttributes.addAll(0, se.getAttributes());
      se = se.getSuperEntity();
    }
    return inheritedAttributes;
  }

  @Override
  public List getSubEntityAttributes() {
    List subAttributes = new ArrayList<>();
    for (Entity sub: getAllSubEntities()) {
      for (Attribute attribute: sub.getAttributes()) {
        if (!attribute.getJavaName().equals(Constants.AN_ID) &&
            !attribute.getJavaName().equals(Constants.AN_SERIAL)) {
          subAttributes.add(attribute);
        }
      }
    }
    return subAttributes;
  }


  @Override
  public List getAttributesIncludingInherited() {
    List allAttributes = new ArrayList<>(getInheritedAttributes());
    allAttributes.addAll(attributes);
    allAttributes = moveIdSerialToEnd(allAttributes);
    return allAttributes;
  }

  @Override
  public List getAttributesIncludingSubEntities() {
    List allAttributes = new ArrayList<>(attributes);
    allAttributes.addAll(getSubEntityAttributes());
    allAttributes = moveIdSerialToEnd(allAttributes);
    return allAttributes;
  }

  @Override
  public List getAllAttributes() {
    List allAttributes = new ArrayList<>(getInheritedAttributes());
    allAttributes.addAll(attributes);
    allAttributes.addAll(getSubEntityAttributes());
    allAttributes = moveIdSerialToEnd(allAttributes);
    return allAttributes;
  }

  @Override
  public List getTableAttributes() {
    InheritanceType ihType = getHierarchyInheritanceType();
    List tableAttributes;
    if (ihType.isMappingToOwnTable()) {
      tableAttributes = new ArrayList<>(attributes);
      if (getSuperEntity() != null) {
        // always add the ID for subclasses in MULTI inheritance
        tableAttributes.add(getAttributeByJavaName(Constants.AN_ID, true));
      }
    }
    else if (ihType.isMappingToSuperTable()) {
      tableAttributes = new ArrayList<>();
      if (isRootOfInheritanceHierarchy()) {
        tableAttributes.addAll(attributes);
        for (Entity child: getAllSubEntities()) {
          tableAttributes.addAll(child.getAttributes());
        }
        tableAttributes = moveIdSerialToEnd(tableAttributes);
      }
    }
    else {
      tableAttributes = getAttributesIncludingInherited();
    }
    return tableAttributes;
  }

  @Override
  public List getMappedAttributes() {
    List mappedAttributes;
    if (isAbstract()) {
      if (inheritanceType.isMappingToOwnTable()) {
        // MULTI
        mappedAttributes = attributes;
      }
      else if (inheritanceType.isMappingToSuperTable()) {
        // SINGLE: strip id and serial. It's mapped by the leafs
        mappedAttributes = removeIdSerial(attributes);
      }
      else  {
        // PLAIN
        mappedAttributes = new ArrayList<>();
      }
    }
    else  {   // leaf
      InheritanceType ihType = getHierarchyInheritanceType();
      if (ihType.isMappingToOwnTable()) {
        // MULTI
        mappedAttributes = attributes;
      }
      else if (ihType.isMappingToSuperTable()) {
        // SINGLE: get id and serial from top of hierarchy
        Entity top = getTopSuperEntity();
        mappedAttributes = appendIdSerial(top, attributes);
      }
      else if (ihType.isMappingToNoTable()) {
        // PLAIN
        mappedAttributes = getAttributesIncludingInherited();
      }
      else  {  // NONE
        mappedAttributes = attributes;
      }
    }
    return mappedAttributes;
  }


  /**
   * Moved id and serial to the end of a new created attribute list.
   *
   * @param attributes the attribute list
   * @return the new filtered attribute list
   */
  protected List moveIdSerialToEnd(List attributes) {
    Attribute idAttribute = null;
    Attribute serialAttribute = null;
    List filteredList = new ArrayList<>();
    for (Attribute attribute: attributes) {
      if (Constants.CN_ID.equals(attribute.getJavaName())) {
        idAttribute = attribute;
        continue;
      }
      if (Constants.CN_SERIAL.equals(attribute.getJavaName())) {
        serialAttribute = attribute;
        continue;
      }
      filteredList.add(attribute);
    }
    if (idAttribute != null) {
      filteredList.add(idAttribute);
    }
    if (serialAttribute != null) {
      filteredList.add(serialAttribute);
    }
    return filteredList;
  }


  /**
   * Removes id and serial from the list of attributes.
   *
   * @param attributes the attribute list
   * @return the new filtered attribute list
   */
  protected List removeIdSerial(List attributes) {
    List filteredList = new ArrayList<>();
    for (Attribute attribute: attributes) {
      if (!Constants.CN_ID.equals(attribute.getJavaName()) &&
          !Constants.CN_SERIAL.equals(attribute.getJavaName())) {
        filteredList.add(attribute);
      }
    }
    return filteredList;
  }

  /**
   * Appends id and serial from another entity.
   *
   * @param entity the entity defining id and serial
   * @param attributes the attribute list
   * @return the new filtered attribute list
   */
  protected List appendIdSerial(Entity entity, List attributes) {
    Attribute idAttribute = null;
    Attribute serialAttribute = null;
    for (Attribute attribute: entity.getAttributes()) {
      if (Constants.CN_ID.equals(attribute.getJavaName())) {
        idAttribute = attribute;
      }
      else if (Constants.CN_SERIAL.equals(attribute.getJavaName())) {
        serialAttribute = attribute;
      }
    }
    List filteredList = new ArrayList<>(attributes);
    if (idAttribute != null) {
      filteredList.add(idAttribute);
    }
    if (serialAttribute != null) {
      filteredList.add(serialAttribute);
    }
    return filteredList;
  }

  @Override
  public List getInheritedIndexes() {
    List inheritedIndexes = new ArrayList<>();
    Entity se = getSuperEntity();
    while (se != null) {
      inheritedIndexes.addAll(0, se.getIndexes());
      se = se.getSuperEntity();
    }
    return inheritedIndexes;
  }

  @Override
  public List getIndexesIncludingInherited() {
    List allIndexes = new ArrayList<>(getInheritedIndexes());
    allIndexes.addAll(indexes);
    return allIndexes;
  }

  @Override
  public List getSubEntityIndexes() {
    List subIndexes = new ArrayList<>();
    for (Entity sub: getSubEntities()) {
      subIndexes.addAll(sub.getIndexes());
    }
    return subIndexes;
  }

  @Override
  public List getIndexesIncludingSubEntities() {
    List allIndexes = new ArrayList<>(indexes);
    allIndexes.addAll(getSubEntityIndexes());
    return allIndexes;
  }

  @Override
  public List getAllIndexes() {
    List allIndexes = new ArrayList<>(getInheritedIndexes());
    allIndexes.addAll(indexes);
    allIndexes.addAll(getSubEntityIndexes());
    return allIndexes;
  }

  @Override
  public List getTableIndexes() {
    InheritanceType ihType = getHierarchyInheritanceType();
    List tableIndexes;
    if (ihType.isMappingToOwnTable()) {
      tableIndexes = indexes;
    }
    else if (ihType.isMappingToSuperTable()) {
      tableIndexes = new ArrayList<>();
      if (isRootOfInheritanceHierarchy()) {
        tableIndexes.addAll(indexes);
        for (Entity child: getAllSubEntities()) {
          tableIndexes.addAll(child.getIndexes());
        }
      }
    }
    else {
      tableIndexes = getIndexesIncludingInherited();
    }
    return tableIndexes;
  }

  @Override
  public List getInheritedRelations() {
    List allRelations = new ArrayList<>();
    Entity se = getSuperEntity();
    while (se != null) {
      allRelations.addAll(0, se.getRelations());
      se = se.getSuperEntity();
    }
    return allRelations;
  }

  @Override
  public List getSubEntityRelations() {
    List allRelations = new ArrayList<>();
    for (Entity sub: getAllSubEntities()) {
      allRelations.addAll(sub.getRelations());
    }
    return allRelations;
  }

  @Override
  public List getRelationsIncludingInherited() {
    List allRelations = new ArrayList<>(getInheritedRelations());
    allRelations.addAll(relations);
    return allRelations;
  }

  @Override
  public List getRelationsIncludingSubEntities() {
    List allRelations = new ArrayList<>(relations);
    allRelations.addAll(getSubEntityRelations());
    return allRelations;
  }

  @Override
  public List getAllRelations() {
    List allRelations = getRelationsIncludingInherited();
    allRelations.addAll(getSubEntityRelations());
    return allRelations;
  }

  @Override
  public List getTableRelations() {
    InheritanceType ihType = getHierarchyInheritanceType();
    List tableRelations;
    if (ihType.isMappingToOwnTable()) {
      tableRelations = relations;
    }
    else if (ihType.isMappingToSuperTable()) {
      tableRelations = new ArrayList<>();
      if (isRootOfInheritanceHierarchy()) {
        tableRelations.addAll(relations);
        for (Entity sub: getAllSubEntities()) {
          tableRelations.addAll(sub.getRelations());
        }
      }
    }
    else {
      tableRelations = getRelationsIncludingInherited();
    }
    return tableRelations;
  }

  @Override
  public Set> getCompositePaths() {
    return compositePaths;
  }

  @Override
  public Set getComponents() {
    Set components = new TreeSet<>();
    for (Relation relation: getRelations()) {
      if (relation.isComposite()) {
        components.add(relation.getForeignEntity());
      }
    }
    return components;
  }

  @Override
  public Set getComponentsIncludingInherited() {
    Set components = new TreeSet<>();
    for (Relation relation: getRelationsIncludingInherited()) {
      if (relation.isComposite()) {
        components.add(relation.getForeignEntity());
      }
    }
    return components;
  }

  @Override
  public Set getAllComponents() {
    Set components = new TreeSet<>();
    for (Relation relation: getRelationsIncludingInherited()) {
      if (relation.isComposite()) {
        Entity component = relation.getForeignEntity();
        components.add(component);
        components.addAll(component.getComponentsIncludingInherited());
      }
    }
    return components;
  }

  @Override
  public boolean isDeeplyReferenced() {
    return deeplyReferenced;
  }

  public void setDeeplyReferenced(boolean deeplyReferenced) {
    this.deeplyReferenced = deeplyReferenced;
  }

  @Override
  public List getDeepReferences() {
    return deepReferences;
  }

  @Override
  public List getDeepReferencesToComponents() {
    return deepReferencesToComponents;
  }

  @Override
  public List getDeeplyReferencedComponents() {
    List deepRelations = new ArrayList<>();
    for (Relation relation : relations) {
      if (relation.isComposite()) {
        Entity component = relation.getForeignEntity();
        if (component.isDeeplyReferenced()) {
          deepRelations.add(relation);
        }
      }
    }
    return deepRelations;
  }

  @Override
  public Attribute getAttributeByJavaName(String javaName, boolean all) {
    if (javaName != null) {
      Entity entity = this;
      while (entity != null) {
        for (Attribute attribute: entity.getAttributes()) {
          if (javaName.equalsIgnoreCase(attribute.getJavaName())) {
            return attribute;
          }
        }
        if (all) {
          entity = entity.getSuperEntity();
        }
        else  {
          break;
        }
      }
    }
    return null;
  }

  @Override
  public Attribute getAttributeByColumnName(String columnName, boolean all) {
    if (columnName != null) {
      columnName = columnName.toLowerCase();
      Entity entity = this;
      while (entity != null) {
        for (Attribute attribute: entity.getAttributes()) {
          if (columnName.equals(attribute.getColumnName())) {
            return attribute;
          }
        }
        if (all) {
          entity = entity.getSuperEntity();
        }
        else  {
          break;
        }
      }
    }
    return null;
  }

  @Override
  public Attribute getContextIdAttribute() {
    return contextIdAttribute;
  }

  @Override
  public List getUniqueDomainKey() {
    if (uniqueDomainKey == null) {
      uniqueDomainKey = new ArrayList<>();
      if (isRootEntityAccordingToModel()) {
        getUniqueDomainKeyImpl(uniqueDomainKey, this);
      }
    }
    return uniqueDomainKey;
  }

  /**
   * Adds the unique domain keys recursively for given entity to the list.
   *
   * @param uniqueDomainKeys the unique domain key attributes
   * @param entity the entity to inspect
   */
  protected void getUniqueDomainKeyImpl(List uniqueDomainKeys, Entity entity) {
    for (Attribute attribute : entity.getAttributesIncludingInherited()) {
      if (attribute.getOptions().isDomainKey()) {
        uniqueDomainKeys.add(attribute);
      }
    }
    for (Entity component: entity.getComponentsIncludingInherited()) {
      if (!component.isRootEntityAccordingToModel()) {   // stop at sub-roots!
        getUniqueDomainKeyImpl(uniqueDomainKeys, component);
      }
    }
  }

  @Override
  public List getSorting() {
    return sorting;
  }

  @Override
  public List getIndexes() {
    return indexes;
  }

  @Override
  public Index getIndex(String name, boolean all) {
    if (name != null) {
      Entity entity = this;
      while (entity != null) {
        for (Index index: entity.getIndexes()) {
          if (name.equalsIgnoreCase(index.getName())) {
            return index;
          }
        }
        if (all) {
          entity = entity.getSuperEntity();
        }
        else  {
          break;
        }
      }
    }
    return null;
  }

  @Override
  public List getRelations() {
    return relations;
  }

  @Override
  public List getReferencingRelations() {
    return referencingRelations;
  }

  @Override
  public List getInheritedReferencingRelations() {
    List inheritedReferencingRelations = new ArrayList<>();
    Entity se = getSuperEntity();
    while (se != null) {
      inheritedReferencingRelations.addAll(0, se.getReferencingRelations());
      se = se.getSuperEntity();
    }
    return inheritedReferencingRelations;
  }

  @Override
  public List getReferencingRelationsIncludingInherited() {
    List allRelations = new ArrayList<>(getInheritedReferencingRelations());
    allRelations.addAll(getReferencingRelations());
    return allRelations;
  }

  @Override
  public List getSubEntityReferencingRelations() {
    List subReferencingRelations = new ArrayList<>();
    for (Entity sub: getAllSubEntities()) {
      subReferencingRelations.addAll(sub.getReferencingRelations());
    }
    return subReferencingRelations;
  }

  @Override
  public List getReferencingRelationsIncludingSubEntities() {
    List allRelations = new ArrayList<>(getReferencingRelations());
    allRelations.addAll(getSubEntityReferencingRelations());
    return allRelations;
  }

  @Override
  public List getAllReferencingRelations() {
    List allRelations = new ArrayList<>(getReferencingRelationsIncludingInherited());
    allRelations.addAll(getSubEntityReferencingRelations());
    return allRelations;
  }

  @Override
  public Relation getRelation(String name, boolean all) {
    if (name != null) {
      Entity entity = this;
      while (entity != null) {
        for (Relation relation: entity.getRelations()) {
          if (name.equalsIgnoreCase(relation.getName())) {
            return relation;
          }
        }
        if (all) {
          entity = entity.getSuperEntity();
        }
        else  {
          break;
        }
      }
    }
    return null;
  }


  /**
   * Validates the entity without relation dependent settings.
   *
   * @throws ModelException if validation failed
   */
  public void validate() throws ModelException {

    if (BasicStringHelper.isAllWhitespace(getName())) {
      throw new ModelException("configuration 'name = ...' missing", this);
    }

    if (!isAbstract()) {
      if (getClassId() == 0) {
        throw new ModelException("configuration 'id = ...' missing", this);
      }
    }
    else  {
      if (getClassId() != 0) {
        throw new ModelException("configuration 'id = ...' not allowed", this);
      }
    }

    getOptions().validate();

    if (getOptions().isRootEntity()) {
      if (getSuperEntityName() != null) {
        throw new ModelException("[ROOT] option cannot be applied to subclasses", this);
      }
      if (getOptions().isRootIdProvided()) {
        throw new ModelException("root entities cannot provide a rootid column", this);
      }
      if (getOptions().isRootClassIdProvided()) {
        throw new ModelException("root entities cannot provide a rootclassid column", this);
      }
    }

    Set javaNames = new HashSet<>();
    Set columnNames = new HashSet<>();
    for (Attribute attribute: getAttributes()) {
      attribute.validate();
      if (!javaNames.add(attribute.getJavaName())) {
        throw new ModelException("duplicate Java name '" + attribute.getJavaName() + "'", this);
      }
      if (!columnNames.add(attribute.getColumnName())) {
        throw new ModelException("duplicate column name '" + attribute.getColumnName() + "'", this);
      }
    }

    Set indexNames = new HashSet<>();
    for (Index index: getIndexes()) {
      index.validate();
      if (!indexNames.add(index.getName())) {
        throw new ModelException("duplicate index '" + index.getName() + "'", this);
      }
    }

    Set relationNames = new HashSet<>();
    for (Relation relation: getRelations()) {
      relation.validate();
      if (!relationNames.add(relation.getName())) {
        throw new ModelException("duplicate relation '" + relation.getName() + "'", this);
      }
    }

  }

  /**
   * Validates the relation dependent settings.
* Assumes {@link ModelImpl#updateRelations} applied successfully. * * @throws ModelException if validation failed */ public void validateRelated() throws ModelException { if (getTableProvidingEntity() == this) { if (BasicStringHelper.isAllWhitespace(getTableName())) { throw new ModelException("configuration 'table = ...' missing", this); } if (getTableName().startsWith("_")) { throw new ModelException("table name '" + getTableName() + "' must not start with an underscore", this); } for (Backend backend: factory.getBackends()) { try { backend.assertValidName("table name", getTableName()); backend.assertValidName("table alias", getTableAlias()); } catch (RuntimeException rex) { throw new ModelException(rex.getMessage(), sourceInfo, rex); } } } else { if (!BasicStringHelper.isAllWhitespace(getTableName())) { throw new ModelException("configuration 'table = ...' not allowed", this); } if (!BasicStringHelper.isAllWhitespace(getTableAlias())) { throw new ModelException("configuration 'alias = ...' not allowed", this); } } if (isRootOfInheritanceHierarchy() && inheritanceType.isMappingToSuperTable()) { // set attributes to nullable (except the ones in root) for (Entity sub: getAllSubEntities()) { for (Attribute attribute: sub.getAttributes()) { ((AttributeImpl) attribute).setNullable(true); } } } if (getIntegrity().isCompositesCheckedByDatabase()) { // either all composite relations must be cascaded or none, not mixed! if (isDeletionCascaded() == null) { throw new ModelException(getIntegrity() + " integrity requires all composite relations to be either cascaded or non-cascaded", this); } } } @Override public String sqlCreateTable(Backend backend) throws ModelException { StringBuilder buf = new StringBuilder(); buf.append(backend.sqlCreateTableIntro(getTableName(), getOptions().getComment())); List tableAttributes = getTableAttributes(); int attributeNum = tableAttributes.size(); int attributeCount = 1; for (Attribute attribute: tableAttributes) { if (!attribute.getOptions().isDerived()) { DataType dataType = attribute.getDataType(); if (dataType == DataType.APPLICATION) { dataType = attribute.getInnerType(); } int multiColumnIndex = 1; int multiColumnSize = dataType.getSqlTypes().length; for (DataType.SqlTypeWithPostfix sp: dataType.getSqlTypesWithPostfix()) { buf.append(" "); String columnName = attribute.getColumnName() + sp.getPostfix(); int size = attribute.getSizeWithDefault(); int scale = attribute.getScaleWithDefault(); Object defaultValue = attribute.getOptions().getDefaultValue(); String comment = attribute.getOptions().getComment(); if (multiColumnIndex > 1) { comment = null; if (defaultValue != null) { defaultValue = sp.getSqlType().getDefaultValue(); } } if (dataType.isScaleInSecondColumn()) { if (multiColumnIndex == 2) { size = 0; } scale = 0; } buf.append(backend.sqlCreateColumn( columnName, comment, sp.getSqlType(), size, scale, attribute.isNullable(), defaultValue, attribute.getJavaName().equals(Constants.AN_ID), attributeCount < attributeNum || multiColumnIndex < multiColumnSize)); multiColumnIndex++; } attributeCount++; } } buf.append(backend.sqlCreateTableClosing(getTableName(), getOptions().getComment())); buf.append(backend.sqlCreateTableComment(getTableName(), getOptions().getComment())); for (Attribute attribute: tableAttributes) { if (!attribute.getOptions().isDerived()) { buf.append(backend.sqlCreateColumnComment(getTableName(), attribute.getColumnName(), attribute.getOptions().getComment())); } } return buf.toString(); } @Override public boolean isComposite() { for (Relation relation: getRelationsIncludingSubEntities()) { if (relation.isComposite()) { return true; } } return false; } @Override public boolean isAbstract() { return inheritanceType.isAbstractClass(); } @Override public boolean isTracked() { return getOptions().getTrackType().isTracked(); } @Override public Boolean isDeletionCascaded() { Boolean cascaded = null; for (Relation relation: getRelations()) { if (relation.isComposite()) { if (cascaded == null) { cascaded = relation.isDeletionCascaded(); } else if (cascaded != relation.isDeletionCascaded()) { return null; // mixed } } } if (cascaded == null) { // no composite relation at all cascaded = false; } return cascaded; } @Override public boolean isRootOfInheritanceHierarchy() { return superEntityName == null && inheritanceType != InheritanceType.NONE; } @Override public Set getRootAttributes() { Set rootAttributes = new HashSet<>(); for (List compositePath: compositePaths) { Relation lastRelation = compositePath.get(compositePath.size() - 1); if (lastRelation.getForeignAttribute() != null) { rootAttributes.add(lastRelation.getForeignAttribute()); } } return rootAttributes; } @Override public Attribute getRootAttribute() { Set rootAttributes = getRootAttributes(); return rootAttributes.size() == 1 ? rootAttributes.iterator().next() : null; } @Override public Set getRootEntities() { Set rootEntities = new HashSet<>(); for (List compositePath: compositePaths) { Relation firstRelation = compositePath.get(0); rootEntities.add(firstRelation.getEntity()); } return rootEntities; } @Override public Entity getRootEntity() { Set rootEntities = getRootEntities(); if (rootEntities.size() == 1) { Entity rootEntity = rootEntities.iterator().next(); if (!rootEntity.isAbstract()) { return rootEntity; } } return null; } @Override public Entity getTableProvidingEntity() { InheritanceType ihType = getHierarchyInheritanceType(); if (ihType.isMappingToOwnTable()) { return this; } if (ihType.isMappingToSuperTable()) { return getTopSuperEntity(); } if (inheritanceType.isMappingToOwnTable()) { return this; } return null; } @Override public String toString() { return getName(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy