/******************************************************************************
* 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.data;
import dbaCore.data.events.Change;
import dbaCore.data.events.ChangeListener;
import javax.xml.bind.annotation.*;
import java.io.Serializable;
import java.util.ArrayList;
/**
* Class representing a Relation
*
* @author Sebastian Theuermann
*/
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement(name = "relation")
@XmlType(propOrder = {"name", "attributes", "functionalDependencies"})
public class RelationSchema extends HistoricObject implements Serializable {
/**
*
*/
private static final long serialVersionUID = -3301439448650204024L;
private String name;
private ArrayList<Attribute> attributes;
private ArrayList<FunctionalDependency> functionalDependencies;
private static int id = 0;
private int ownId;
public RelationSchema() {
super();
ownId = id++;
attributes = new ArrayList<>();
functionalDependencies = new ArrayList<>();
super.changeListener = new ChangeListener() {
@Override
public void Change(Change change) {
changeSupport.fireChange(change.getTime());
}
};
}
public RelationSchema(String name) {
this();
this.name = name;
}
public RelationSchema(String name, ArrayList<Attribute> attributes) {
this(name);
this.attributes = attributes;
}
public RelationSchema(String name, ArrayList<Attribute> attributes, ArrayList<FunctionalDependency>
functionalDependencies) {
this(name, attributes);
this.functionalDependencies = functionalDependencies;
}
public String getName() {
return name;
}
public void setName(String name) {
if (!name.equals(this.name)) {
changeSupport.fireBeforeChange();
this.name = name;
changeSupport.fireAfterChange();
}
}
/**
* Assigns the new name without firing the changeEvent!
*
* @param name the new name for the RelationSchema
*/
public void setNameWithoutFiring(String name) {
if (!name.equals(this.name)) {
this.name = name;
}
}
/**
* Renames a given Attribute without firing Change
*
* @param attribute the Attribute to rename
* @param newName the new name for the attribute
*/
public void renameAttributeWithoutFiring(Attribute attribute, String newName) {
if (attributes.contains(attribute)) {
attribute.setNameWithoutFiring(newName);
}
}
public void setOwnId(int ownId) {
this.ownId = ownId;
}
@XmlTransient
public int getOwnId() {
return ownId;
}
@XmlElementWrapper(name = "attributes")
@XmlElement(name = "attribute")
public ArrayList<Attribute> getAttributes() {
return attributes;
}
/**
* Returns a array containing the names of all Attributes of the
* relation
*
* @return a array of the attribute-names
*/
public String[] getAttributesNameArray() {
String[] attrArray = new String[attributes.size()];
int i = 0;
for (Attribute attr : attributes) {
attrArray[i++] = attr.getName();
}
return attrArray;
}
/**
* Returns a Attribute of the given name
*
* @param name the name of the attribute to look for
* @return the attribute with the given name
*/
public Attribute getAttributeByName(String name) {
for (Attribute attr : attributes) {
if (attr.getName().equals(name)) {
return attr;
}
}
return null;
}
@XmlElementWrapper(name = "functionaldependencies")
@XmlElement(name = "fd")
public ArrayList<FunctionalDependency> getFunctionalDependencies() {
return functionalDependencies;
}
public void setAttributes(ArrayList<Attribute> attributes) {
changeSupport.fireBeforeChange();
this.attributes = attributes;
changeSupport.fireAfterChange();
}
public void removeAttribute(Attribute attribute) {
changeSupport.fireBeforeChange();
attribute.removeChangeListener(changeListener);
attributes.remove(attribute);
updateFunctionalDependencies();
changeSupport.fireAfterChange();
}
public void setFunctionalDependencies(ArrayList<FunctionalDependency> functionalDependencies) {
changeSupport.fireBeforeChange();
this.functionalDependencies = functionalDependencies;
changeSupport.fireAfterChange();
}
public boolean addFunctionalDependency(FunctionalDependency dependency) {
for (FunctionalDependency fd : functionalDependencies) {
if (fd.equals(dependency)) {
return false;
}
}
changeSupport.fireBeforeChange();
functionalDependencies.add(dependency);
changeSupport.fireAfterChange();
return true;
}
public boolean addAttribute(String name) {
for (Attribute attribute : attributes) {
if (attribute.getName().equalsIgnoreCase(name)) {
return false;
}
}
return addAttribute(new Attribute(name));
}
public boolean addAttribute(Attribute attribute) {
if (!attributes.contains(attribute)) {
attribute.addChangeListener(changeListener);
changeSupport.fireBeforeChange();
attributes.add(attribute);
changeSupport.fireAfterChange();
return true;
}
return false;
}
public void removeFunctionalDependency(FunctionalDependency dependency) {
changeSupport.fireBeforeChange();
functionalDependencies.remove(dependency);
changeSupport.fireAfterChange();
}
public void updateFunctionalDependencies() {
ArrayList<FunctionalDependency> toDelete = new ArrayList<>();
for (FunctionalDependency fd : functionalDependencies) {
cleanReferences(fd.getSourceAttributes());
cleanReferences(fd.getTargetAttributes());
if (fd.getSourceAttributes().isEmpty() || fd.getTargetAttributes().isEmpty()) {
toDelete.add(fd);
}
}
functionalDependencies.removeAll(toDelete);
}
/**
* Removes zombie-Attributes from the functionalDependencies
*
* @param list the Attributes to check
*/
private void cleanReferences(ArrayList<Attribute> list) {
ArrayList<Attribute> toDelete = new ArrayList<>();
for (Attribute item : list) {
if (!isAttributeExisting(item)) {
toDelete.add(item);
}
}
for (Attribute attr : toDelete) {
list.remove(attr);
}
}
/**
* Returns if a given Attribute exists in the Relation
*
* @param attribute the Attribute to check
* @return true if it exists, false if not
*/
private boolean isAttributeExisting(Attribute attribute) {
for (Attribute attr : attributes) {
if (attr == attribute) {
return true;
}
}
return false;
}
@Override
public String toString() {
return name + " (" + Utilities.getStringFromArrayList(attributes) + ")";
}
@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;
}
@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;
}
/**
* Initializes Attribute-ChangeListeners by adding them to the
* Attributes of the relation
*/
public void initPropertyChangeListeners() {
for (Attribute attribute : attributes) {
attribute.addChangeListener(changeListener);
}
}
/**
* Restores the References between the FunctionalDependencies and
* the Attributes of the relation
*/
public void restoreReferences() {
for (FunctionalDependency fd : getFunctionalDependencies()) {
for (int i = 0; i < fd.getSourceAttributes().size(); i++) {
fd.getSourceAttributes().set(i, getAttributeByName(fd.getSourceAttributes().get(i).getName()));
}
for (int i = 0; i < fd.getTargetAttributes().size(); i++) {
fd.getTargetAttributes().set(i, getAttributeByName(fd.getTargetAttributes().get(i).getName()));
}
}
}
}