@Override
  public RelationSchema getClone() {
    RelationSchema clone;

    ArrayList<Attribute> clonesAttributes = new ArrayList<>();
    ArrayList<FunctionalDependency> clonesFunctionalDependencies = new ArrayList<>();

    for (Attribute attribute : attributes) {
      clonesAttributes.add((Attribute) attribute.getClone());
    }

    for (FunctionalDependency dependency : functionalDependencies) {
      clonesFunctionalDependencies.add(dependency.getClone());
    }

    clone = new RelationSchema(name, clonesAttributes, clonesFunctionalDependencies);

    clone.setOwnId(ownId);

    clone.restoreReferences();
    clone.initPropertyChangeListeners();

    clone.setDirty(super.isDirty());

    return clone;
  }
  /**
   * Walks through all Fd's an united the fd's with the same left side
   *
   * @param relation the RelationSchema as source for the Fd's
   */
  private void uniteFdsWithSameLeftSide(RelationSchema relation) {
    ArrayList<FunctionalDependency> compactedFds = new ArrayList<>();

    for (FunctionalDependency fd : relation.getFunctionalDependencies()) {
      if (!containsLeftSide(compactedFds, fd)) {
        compactedFds.add(fd);
      } else {
        uniteWithExistingLeftSide(compactedFds, fd);
      }
    }

    relation.setFunctionalDependencies(compactedFds);
  }
  @Override
  public boolean equals(Object object) {
    if (object instanceof RelationSchema) {
      RelationSchema otherSchema = (RelationSchema) object;

      if (!name.equals(otherSchema.getName())) {
        return false;
      }

      if (!attributes.equals(otherSchema.getAttributes())) {
        return false;
      }

      if (!functionalDependencies.equals(otherSchema.getFunctionalDependencies())) {
        return false;
      }
    }

    return true;
  }
  public NormalizationResult normalize(
      RelationSchema relationToNormalize, NormalForm actualNF, NormalForm targetNF) {
    RelationSchema relation = relationToNormalize.getClone();
    RelationSchema backupRelation;

    NormalizationResult result;

    // Pick up lonely Attributes (in no Functional dependency)
    RelationUtils.getInstance().determineAllAttributes(relation);

    // Set primary-key if not present
    if (RelationUtils.getInstance().getPrimaryKey(relation).getAttributes().isEmpty()) {
      Key newPrimaryKey = RelationUtils.getInstance().getKey(relation);
      for (Attribute attribute : relation.getAttributes()) {
        if (newPrimaryKey.getAttributes().contains(attribute)) {
          attribute.setIsPrimaryKey(true);
        }
      }
    }

    backupRelation = relation.getClone();

    normalizationProcedures.clear();
    switch (actualNF) {
      case FIRST:
        switch (targetNF) {
          case SECOND:
            // 1.NF ==> 2.NF
            normalizationProcedures.add(new DecompositionTo2NF());

            break;
          case THIRD:
            // 1.NF ==> 3.NF
            normalizationProcedures.add(new SyntheseTo3NF());
            break;
          case BOYCECODD:
            // 1.NF ==> BCNF
            normalizationProcedures.add(new SyntheseTo3NF());
            normalizationProcedures.add(new DecompositionToBCNF());
            break;
          default:
            throw new IllegalArgumentException();
        }
        break;
      case SECOND:
        switch (targetNF) {
          case THIRD:
            // 2.NF ==> 3.NF
            normalizationProcedures.add(new SyntheseTo3NF());
            break;
          case BOYCECODD:
            // 2.NF ==> BCNF
            normalizationProcedures.add(new SyntheseTo3NF());
            normalizationProcedures.add(new DecompositionToBCNF());
            break;
          default:
            throw new IllegalArgumentException();
        }
        break;
      case THIRD:
        // 3.NF ==> BCNF
        normalizationProcedures.add(new DecompositionToBCNF());
        break;
      default:
        throw new IllegalArgumentException();
    }

    result = startNormalizing(relation, normalizationProcedures, targetNF);

    if (result.getRelations().isEmpty()) {
      result.getRelations().add(backupRelation);
    }

    return result;
  }