/******************************************************************************
* Copyright: GPL v3 *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
******************************************************************************/
package dbaCore.logic.normalization;
import dbaCore.data.*;
import dbaCore.logic.Analysis.GeneralRelationCheck;
import dbaCore.logic.Analysis.RelationUtils;
import java.util.ArrayList;
/**
* Class providing basic methods for relation-optimization
*
* @author Sebastian Theuermann
*/
public class Optimizer {
private ArrayList<NormalizationAlgorithm> normalizationProcedures;
protected GeneralRelationCheck checker;
public Optimizer() {
super();
normalizationProcedures = new ArrayList<>();
checker = new GeneralRelationCheck();
}
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;
}
/**
* Executes the normalization procedures defined in "procedures" on
* the given schema, exits when the targetNf is reached
*
* @param schema the relation to normalize
* @return the results of the Normalization
*/
private NormalizationResult startNormalizing(RelationSchema schema, ArrayList<NormalizationAlgorithm> procedures,
NormalForm targetNf) {
NormalizationResult result = new NormalizationResult();
NormalizationResult tempResult;
result.getRelations().add(schema);
for (NormalizationAlgorithm procedure : procedures) {
// Quit if NF already reached
if (checker.getNF(result.getRelations()).ordinal() >= targetNf.ordinal()) {
break;
}
tempResult = (NormalizationResult) result.getClone();
for (RelationSchema relation : tempResult.getRelations()) {
procedure.normalize(relation, result, false);
}
}
compactFds(result);
return result;
}
/**
* Compacts all Fd's of the given Relations
*
* @param result the NormalizationResult to work with
*/
private void compactFds(NormalizationResult result) {
for (RelationSchema relation : result.getRelations()) {
uniteFdsWithSameLeftSide(relation);
}
}
/**
* 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);
}
/**
* Returns if a Fd with the same left side is already in the given
* list
*
* @param fds the ArrayList to look inside
* @param newFd the fd with the left-side to look for
* @return true if the ArrayList contains a fd with the same left
* side, false if not
*/
private boolean containsLeftSide(ArrayList<FunctionalDependency> fds, FunctionalDependency newFd) {
for (FunctionalDependency fd : fds) {
if (fd.getSourceAttributes().equals(newFd.getSourceAttributes())) {
return true;
}
}
return false;
}
/**
* Adds the TargetAttributes of the newFd to the TargetAttributes of
* a existing Fd with the same left side
*
* @param fds a ArrayList with the existing FunctionalDependencies
* @param newFd the FunctionalDependency to unite with a existing one
* @return true for success, false if the was no existing Fd with
* the same left side
*/
private boolean uniteWithExistingLeftSide(ArrayList<FunctionalDependency> fds, FunctionalDependency newFd) {
for (FunctionalDependency fd : fds) {
if (fd.getSourceAttributes().equals(newFd.getSourceAttributes())) {
fd.getTargetAttributes().addAll(newFd.getTargetAttributes());
return true;
}
}
return false;
}
}