/******************************************************************************
* 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.Analysis;
import dbaCore.data.*;
import java.util.ArrayList;
/**
* Basic Methods to analyze a relation
*
* @author Sebastian Theuermann
*/
public class RelationUtils {
private static RelationUtils instance = new RelationUtils();
// Avoid initialization with "new RelationUtils();"
private RelationUtils() {
super();
}
// Threadsafety first!
public static synchronized RelationUtils getInstance() {
return instance;
}
/**
* Returns a name for a new Relation
*
* @param name name of the parent-relation
* @param schema the RelationSchema to work with
* @return a new valid name for a relationSchema
*/
public String getRelationName(String name, ArrayList<RelationSchema> schema) {
int i = 1;
while (relationNameAlreadyExists(name + String.valueOf(i), schema)) {
i++;
}
return name + String.valueOf(i);
}
/**
* Returns if a given Relation-name already exists
*
* @param name the name to look for
* @param schema the RelationSchema to work with
* @return true if the name already exists/ false if not
*/
public boolean relationNameAlreadyExists(String name, ArrayList<RelationSchema> schema) {
for (RelationSchema rel : schema) {
if (rel.getName().equals(name)) {
return true;
}
}
return false;
}
/**
* Returns the CandidateKey with fewest Attributes s
*
* @param schema the RelationSchema to get the Keys from
* @return the "smallest" Key
*/
public Key getKey(RelationSchema schema) {
GeneralRelationCheck checker = new GeneralRelationCheck();
return getKey(checker.getAllCandidateKeys(schema));
}
/**
* Returns the smallest of all candidateKeys
*
* @param keyList a list with all candidateKeys
* @return the smallest candidateKey
*/
public Key getKey(ArrayList<Key> keyList) {
Key result = new Key();
if (!keyList.isEmpty()) {
result = keyList.get(0);
for (Key key : keyList) {
if (key.getAttributes().size() < result.getAttributes().size()) {
result = key;
}
}
}
return result;
}
/**
* Returns a Key made of all Attributes marked as PK
*
* @param schema the relation to work with
* @return a key (all Attributes with pk=true)
*/
public Key getPrimaryKey(RelationSchema schema) {
Key result = new Key();
for (Attribute attr : schema.getAttributes()) {
if (attr.getIsPrimaryKey()) {
result.getAttributes().add(attr);
}
}
return result;
}
/**
* Restores Attributes of the given relation with the attributes of
* the functionalDependencies
*
* @param schema the relation to work with
*/
public void restoreAttributesByFds(RelationSchema schema) {
ArrayList<Attribute> allAttributes = new ArrayList<>();
for (FunctionalDependency fd : schema.getFunctionalDependencies()) {
for (Attribute attr : fd.getSourceAttributes()) {
allAttributes.add(attr);
}
for (Attribute attr : fd.getTargetAttributes()) {
allAttributes.add(attr);
}
}
for (Attribute attribute : allAttributes) {
if (!schema.getAttributes().contains(attribute)) {
schema.addAttribute(attribute);
}
}
}
/**
* Checks if at least one side of the given Fd contains a given
* Attribute
*
* @param fd Functional Dependency to work with
* @param attributes Attributes to look for
* @return true if the fd contains the attributes, false if not
*/
public boolean isFdContainingGivenAttributes(FunctionalDependency fd, ArrayList<Attribute> attributes) {
for (Attribute attribute : attributes) {
if (fd.getSourceAttributes().contains(attribute)) {
return true;
} else if (fd.getTargetAttributes().contains(attribute)) {
return true;
}
}
return false;
}
/**
* Unites all fd's with equal left sides
*
* @param fdList to work with
*/
public void uniteFdsWithSameLeftSide(ArrayList<FunctionalDependency> fdList) {
ArrayList<FunctionalDependency> fdsToDelete = new ArrayList<>();
for (FunctionalDependency fd : fdList) {
// Don't deal with items that will be deleted shortly
if (fdsToDelete.contains(fd)) {
continue;
}
for (FunctionalDependency subFd : fdList) {
if (fdsToDelete.contains(fd) || subFd == fd) {
continue;
}
if (fd.getSourceAttributes().equals(subFd.getSourceAttributes())) {
fd.getTargetAttributes().addAll(subFd.getTargetAttributes());
fdsToDelete.add(subFd);
}
}
}
fdList.removeAll(fdsToDelete);
}
/**
* Resets the Primary Key to the optimal choice
*/
public void resetPrimaryKey(RelationSchema schema) {
Key key = getKey(schema);
for (Attribute attr : schema.getAttributes()) {
attr.setIsPrimaryKey(key.getAttributes().contains(attr));
}
}
/**
* Returns the text for a given Normalform
*
* @param nf the nf to get a text for
* @return a String representing the given nf
*/
public String getNormalFormText(NormalForm nf) {
switch (nf) {
case BOYCECODD:
return "BCNF";
case THIRD:
return "3.NF";
case SECOND:
return "2.NF";
default:
return "1.NF";
}
}
/**
* Let all undetermined Attributes be determined by the primaryKey
*
* @param relation the relation to work with
*/
public void determineAllAttributes(RelationSchema relation) {
Key primaryKey = RelationUtils.getInstance().getPrimaryKey(relation);
ArrayList<Attribute> targetAttributes = new ArrayList<>();
ArrayList<Attribute> determinedAttributes = getDeterminedAttributes(relation);
for (Attribute attr : relation.getAttributes()) {
if (!primaryKey.getAttributes().contains(attr)) {
if (!determinedAttributes.contains(attr)) {
targetAttributes.add(attr);
}
}
}
if (!targetAttributes.isEmpty()) {
relation.addFunctionalDependency(new FunctionalDependency(primaryKey.getAttributes(), targetAttributes));
}
relation.updateFunctionalDependencies();
}
/**
* Returns a ArrayList of Attributes which are determined by at
* least one fd
*
* @param relation the relation to work with
* @return a ArrayList with all Attributes that are determined by
* something
*/
public ArrayList<Attribute> getDeterminedAttributes(RelationSchema relation) {
ArrayList<Attribute> determinedAttributes = new ArrayList<>();
for (FunctionalDependency fd : relation.getFunctionalDependencies()) {
determinedAttributes.addAll(fd.getTargetAttributes());
}
return determinedAttributes;
}
}