/****************************************************************************** * 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.Attribute; import dbaCore.data.FunctionalDependency; import dbaCore.data.NormalizationResult; import dbaCore.data.RelationSchema; import dbaCore.logic.Analysis.RelationUtils; import java.util.ArrayList; /** * Normalization to BoyceCodd-NormalForm using Decomposition * * @author Sebastian Theuermann */ public class DecompositionToBCNF extends Decomposition { @Override public void normalize(RelationSchema relationToNormalize, NormalizationResult result, Boolean minimizeFds) { if (minimizeFds) { normalizeRelation(getMinimalRelation(relationToNormalize), result); } else { normalizeRelation(relationToNormalize, result); } } private void normalizeRelation(RelationSchema relationToNormalize, NormalizationResult result) { RelationSchema newSchema; ArrayList<FunctionalDependency> violatingFds = checker.checkForBCNF(relationToNormalize); FunctionalDependency violatingFd; // If there is no FD violating BCNF ==> nothing to do here if (relationToNormalize.getFunctionalDependencies().isEmpty() || violatingFds.isEmpty()) { // Remove duplicate entry of relation in results for (RelationSchema relation : result.getRelations()) { if (relation.equals(relationToNormalize)) { result.getRelations().remove(relation); break; } } result.getRelations().add(relationToNormalize); return; } // FD Y ==> A violates BCNF newSchema = new RelationSchema(); newSchema.setName(RelationUtils.getInstance().getRelationName(relationToNormalize.getName(), result.getRelations())); ArrayList<Attribute> onlyDeterminedAttributes = checker.getOnlyDeterminedAttributes(relationToNormalize); violatingFd = violatingFds.get(0); for (FunctionalDependency fd : violatingFds) { if (onlyDeterminedAttributes.containsAll(fd.getTargetAttributes())) { violatingFd = fd; break; } } // S2 = YA // S1 = S - A transferFd(relationToNormalize, newSchema, violatingFd); // Update keys updatePrimaryAndForeignKeys(relationToNormalize, newSchema, result.getForeignKeys()); // Recursive calls normalizeRelation(newSchema, result); normalizeRelation(relationToNormalize, result); } /** * Moves a FD and it's Attributes from one RelationSchema to another * * @param oldSchema the RelationSchema to cut from * @param newSchema the RelationSchema to paste into * @param fd the FunctionalDependency to move */ private void transferFd(RelationSchema oldSchema, RelationSchema newSchema, FunctionalDependency fd) { oldSchema.removeFunctionalDependency(fd); // Copy everything to new schema for (Attribute sourceAttribute : fd.getSourceAttributes()) { newSchema.addAttribute((Attribute) sourceAttribute.getClone()); } for (Attribute targetAttribute : fd.getTargetAttributes()) { newSchema.addAttribute((Attribute) targetAttribute.getClone()); } newSchema.getFunctionalDependencies().add(fd.getClone()); // reconnect attributes with functional dependencies newSchema.restoreReferences(); // Remove copied things in the old schema oldSchema.getAttributes().removeAll(fd.getTargetAttributes()); ArrayList<FunctionalDependency> fdsToDelete = new ArrayList<>(); ArrayList<Attribute> attributesToDelete = new ArrayList<>(); for (FunctionalDependency oldFd : oldSchema.getFunctionalDependencies()) { // Taking care of the TargetAttributes for (Attribute attr : oldFd.getTargetAttributes()) { if (!oldSchema.getAttributes().contains(attr)) { attributesToDelete.add(attr); } } while (!attributesToDelete.isEmpty()) { oldFd.getTargetAttributes().remove(attributesToDelete.get(0)); attributesToDelete.remove(0); } if (oldFd.getTargetAttributes().isEmpty()) { fdsToDelete.add(oldFd); } // Taking care of the SourceAttributes for (Attribute targetAttr : oldFd.getSourceAttributes()) { if (fdsToDelete.contains(oldFd)) { continue; } if (!oldSchema.getAttributes().contains(targetAttr)) { fdsToDelete.add(oldFd); } } } for (FunctionalDependency candidate : fdsToDelete) { oldSchema.removeFunctionalDependency(candidate); } RelationUtils.getInstance().restoreAttributesByFds(oldSchema); } }