diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java index 1cea555..f068a5f 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java @@ -1,10 +1,7 @@ package nl.andrewlalis.erme.control.actions.edits; import lombok.Setter; -import nl.andrewlalis.erme.model.Attribute; -import nl.andrewlalis.erme.model.AttributeType; -import nl.andrewlalis.erme.model.MappingModel; -import nl.andrewlalis.erme.model.Relation; +import nl.andrewlalis.erme.model.*; import javax.swing.*; import java.awt.*; @@ -49,6 +46,7 @@ public class AddAttributeAction extends AbstractAction { Relation r = selectedRelations.get(0); Component c = (Component) e.getSource(); String name = JOptionPane.showInputDialog(c, "Enter the name of the attribute.", "Attribute Name", JOptionPane.PLAIN_MESSAGE); + if (name == null) return; Integer index = (Integer) JOptionPane.showInputDialog( c, "Select the index to insert this attribute at.", @@ -67,7 +65,38 @@ public class AddAttributeAction extends AbstractAction { AttributeType.values(), AttributeType.PLAIN ); - if (name != null && index != null && type != null) { + if (type.equals(AttributeType.FOREIGN_KEY)) { + if (this.model.getRelations().size() < 2) { + JOptionPane.showMessageDialog(c, "There should be at least 2 relations present in the model.", "Not Enough Relations", JOptionPane.WARNING_MESSAGE); + return; + } + Relation fkRelation = (Relation) JOptionPane.showInputDialog( + c, + "Select the relation that this foreign key references.", + "Foreign Key Relation Reference", + JOptionPane.PLAIN_MESSAGE, + null, + this.model.getRelations().toArray(new Relation[0]), + this.model.getRelations().stream().findFirst().orElse(null) + ); + List eligibleAttributes = fkRelation.getReferencableAttributes(); + if (eligibleAttributes.isEmpty()) { + JOptionPane.showMessageDialog(c, "There are no referencable attributes in the selected relation.", "No Referencable Attributes", JOptionPane.WARNING_MESSAGE); + return; + } + Attribute fkAttribute = (Attribute) JOptionPane.showInputDialog( + c, + "Select the attribute that this foreign key references.", + "Foreign Key Attribute Reference", + JOptionPane.PLAIN_MESSAGE, + null, + eligibleAttributes.toArray(new Attribute[0]), + eligibleAttributes.get(0) + ); + if (fkAttribute != null) { + r.addAttribute(new ForeignKeyAttribute(r, name, fkAttribute)); + } + } else { r.addAttribute(new Attribute(r, type, name), index); } } diff --git a/src/main/java/nl/andrewlalis/erme/model/Attribute.java b/src/main/java/nl/andrewlalis/erme/model/Attribute.java index b1929f8..d35fb17 100644 --- a/src/main/java/nl/andrewlalis/erme/model/Attribute.java +++ b/src/main/java/nl/andrewlalis/erme/model/Attribute.java @@ -1,6 +1,7 @@ package nl.andrewlalis.erme.model; import lombok.Getter; +import nl.andrewlalis.erme.view.view_models.AttributeViewModel; import java.io.Serializable; import java.util.Objects; @@ -14,6 +15,8 @@ public class Attribute implements Serializable { private AttributeType type; private String name; + private AttributeViewModel viewModel; + public Attribute(Relation relation, AttributeType type, String name) { this.relation = relation; this.type = type; @@ -30,6 +33,13 @@ public class Attribute implements Serializable { this.relation.getModel().fireChangedEvent(); } + public AttributeViewModel getViewModel() { + if (this.viewModel == null) { + this.viewModel = new AttributeViewModel(this); + } + return this.viewModel; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/nl/andrewlalis/erme/model/ForeignKeyAttribute.java b/src/main/java/nl/andrewlalis/erme/model/ForeignKeyAttribute.java new file mode 100644 index 0000000..1ccb2d0 --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/model/ForeignKeyAttribute.java @@ -0,0 +1,25 @@ +package nl.andrewlalis.erme.model; + +import lombok.Getter; + +@Getter +public class ForeignKeyAttribute extends Attribute { + private Attribute reference; + + public ForeignKeyAttribute(Relation relation, String name, Attribute reference) { + super(relation, AttributeType.FOREIGN_KEY, name); + this.reference = reference; + } + + public ForeignKeyAttribute(Relation relation, String name, String referencedRelationName, String referencedAttributeName) { + this(relation, name, relation.getModel().findAttribute(referencedRelationName, referencedAttributeName)); + if (this.getReference() == null) { + throw new IllegalArgumentException("Unknown attribute name."); + } + } + + public void setReference(Attribute reference) { + this.reference = reference; + this.getRelation().getModel().fireChangedEvent(); + } +} diff --git a/src/main/java/nl/andrewlalis/erme/model/MappingModel.java b/src/main/java/nl/andrewlalis/erme/model/MappingModel.java index b765687..9275243 100644 --- a/src/main/java/nl/andrewlalis/erme/model/MappingModel.java +++ b/src/main/java/nl/andrewlalis/erme/model/MappingModel.java @@ -40,6 +40,18 @@ public class MappingModel implements Serializable { return this.relations.stream().filter(Relation::isSelected).collect(Collectors.toList()); } + public Attribute findAttribute(String relationName, String attributeName) { + for (Relation r : this.getRelations()) { + if (!r.getName().equals(relationName)) continue; + for (Attribute a : r.getAttributes()) { + if (a.getName().equals(attributeName)) { + return a; + } + } + } + return null; + } + public void addChangeListener(ModelChangeListener listener) { if (this.changeListeners == null) { this.changeListeners = new HashSet<>(); diff --git a/src/main/java/nl/andrewlalis/erme/model/Relation.java b/src/main/java/nl/andrewlalis/erme/model/Relation.java index 7cbdbbe..f082d52 100644 --- a/src/main/java/nl/andrewlalis/erme/model/Relation.java +++ b/src/main/java/nl/andrewlalis/erme/model/Relation.java @@ -8,6 +8,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * Represents a single "relation" or table in the diagram. @@ -64,6 +65,12 @@ public class Relation implements Serializable { return this.viewModel; } + public List getReferencableAttributes() { + return this.attributes.stream() + .filter(a -> a.getType() == AttributeType.ID_KEY || a.getType() == AttributeType.PARTIAL_ID_KEY) + .collect(Collectors.toList()); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java b/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java index b8b548e..269aa5c 100644 --- a/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java +++ b/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java @@ -1,9 +1,6 @@ package nl.andrewlalis.erme.view; -import nl.andrewlalis.erme.model.Attribute; -import nl.andrewlalis.erme.model.AttributeType; -import nl.andrewlalis.erme.model.MappingModel; -import nl.andrewlalis.erme.model.Relation; +import nl.andrewlalis.erme.model.*; import javax.swing.*; import java.awt.*; @@ -24,7 +21,7 @@ public class EditorFrame extends JFrame { model.addRelation(usersRelation); Relation tokensRelation = new Relation(model, new Point(50, 120), "Tokens"); tokensRelation.addAttribute(new Attribute(tokensRelation, AttributeType.ID_KEY, "tokenCode")); - tokensRelation.addAttribute(new Attribute(tokensRelation, AttributeType.FOREIGN_KEY, "username")); + tokensRelation.addAttribute(new ForeignKeyAttribute(tokensRelation,"username", "Users", "username")); tokensRelation.addAttribute(new Attribute(tokensRelation, AttributeType.PLAIN, "expirationDate")); model.addRelation(tokensRelation); diff --git a/src/main/java/nl/andrewlalis/erme/view/view_models/RelationViewModel.java b/src/main/java/nl/andrewlalis/erme/view/view_models/RelationViewModel.java index 5d1a247..a4317e0 100644 --- a/src/main/java/nl/andrewlalis/erme/view/view_models/RelationViewModel.java +++ b/src/main/java/nl/andrewlalis/erme/view/view_models/RelationViewModel.java @@ -25,7 +25,7 @@ public class RelationViewModel implements ViewModel { g.setColor(Color.BLACK); g.drawString(as.getIterator(), bounds.x + PADDING_X, bounds.y + this.getNameBounds(g).height - PADDING_Y); for (Attribute a : this.relation.getAttributes()) { - new AttributeViewModel(a).draw(g); + a.getViewModel().draw(g); } if (this.relation.isSelected()) { g.setColor(Color.BLUE); @@ -40,7 +40,7 @@ public class RelationViewModel implements ViewModel { int totalAttributeWidth = 0; int maxAttributeHeight = 0; for (Attribute a : this.relation.getAttributes()) { - Rectangle attributeBounds = new AttributeViewModel(a).getBounds(g); + Rectangle attributeBounds = a.getViewModel().getBounds(g); totalAttributeWidth += attributeBounds.width; maxAttributeHeight = Math.max(maxAttributeHeight, attributeBounds.height); }