From 7e22602b574f25fc1a8dc4feff0dc119867d8997 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 7 Feb 2021 22:28:51 +0100 Subject: [PATCH] Added better stuff and new model. --- README.md | 14 +++++- design/main_interface.PNG | Bin 0 -> 9943 bytes .../erme/control/actions/NewModelAction.java | 35 +++++++++++++++ .../actions/edits/AddAttributeAction.java | 10 ++++- .../actions/edits/RemoveAttributeAction.java | 4 +- .../nl/andrewlalis/erme/model/Attribute.java | 3 +- .../andrewlalis/erme/model/AttributeType.java | 3 +- .../erme/model/ForeignKeyAttribute.java | 17 ++++++-- .../andrewlalis/erme/model/MappingModel.java | 15 +++++++ .../nl/andrewlalis/erme/model/Relation.java | 6 +++ .../andrewlalis/erme/view/DiagramPanel.java | 2 + .../erme/view/DiagramPopupMenu.java | 1 - .../nl/andrewlalis/erme/view/EditorFrame.java | 2 +- .../andrewlalis/erme/view/EditorMenuBar.java | 9 ++++ .../view/view_models/AttributeViewModel.java | 40 ++++++++++++------ .../view/view_models/RelationViewModel.java | 5 ++- 16 files changed, 136 insertions(+), 30 deletions(-) create mode 100644 design/main_interface.PNG create mode 100644 src/main/java/nl/andrewlalis/erme/control/actions/NewModelAction.java diff --git a/README.md b/README.md index b5cc030..421e4cf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ -# EntityRelationMappingEditor -Simple GUI tool to create entity-relational mapping diagrams. +# Entity-Relation Mapping Editor +A simple UI for editing entity-relation mapping diagrams. + + + +## How to Use +The interface and menus should be pretty self-explanatory, but here are some tips to get you started: +* Click on a relation to select it. +* You can CTRL + click to select multiple relations. +* Drag the relations to move them around in the window. +* Right-click on different areas to access context menus with some helpful actions. +* When exporting the model to an image, be sure to add the desired image file extension (`.png`, `.jpg`, `.bmp`, etc.), or the application will default to `.png`. diff --git a/design/main_interface.PNG b/design/main_interface.PNG new file mode 100644 index 0000000000000000000000000000000000000000..bca62de234cbaf9d05d67025c12431b607eb75f5 GIT binary patch literal 9943 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 literal 0 HcmV?d00001 diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/NewModelAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/NewModelAction.java new file mode 100644 index 0000000..517ccbb --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/control/actions/NewModelAction.java @@ -0,0 +1,35 @@ +package nl.andrewlalis.erme.control.actions; + +import lombok.Setter; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.view.DiagramPanel; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +public class NewModelAction extends AbstractAction { + private static NewModelAction instance; + + public static NewModelAction getInstance() { + if (instance == null) { + instance = new NewModelAction(); + } + return instance; + } + + @Setter + private DiagramPanel diagramPanel; + + public NewModelAction() { + super("New Model"); + this.putValue(SHORT_DESCRIPTION, "Create a new model."); + this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + this.diagramPanel.setModel(new MappingModel()); + } +} 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 f068a5f..053b138 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 @@ -65,7 +65,13 @@ public class AddAttributeAction extends AbstractAction { AttributeType.values(), AttributeType.PLAIN ); - if (type.equals(AttributeType.FOREIGN_KEY)) { + boolean shouldUseForeignKey = JOptionPane.showConfirmDialog( + c, + "Is this attribute a foreign key?", + "Foreign Key", + JOptionPane.YES_NO_OPTION + ) == JOptionPane.YES_OPTION; + if (shouldUseForeignKey) { 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; @@ -94,7 +100,7 @@ public class AddAttributeAction extends AbstractAction { eligibleAttributes.get(0) ); if (fkAttribute != null) { - r.addAttribute(new ForeignKeyAttribute(r, name, fkAttribute)); + r.addAttribute(new ForeignKeyAttribute(r, type, name, fkAttribute), index); } } else { r.addAttribute(new Attribute(r, type, name), index); diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java index a5ad92b..227aff9 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java @@ -47,8 +47,8 @@ public class RemoveAttributeAction extends AbstractAction { Relation r = selectedRelations.get(0); Attribute attribute = (Attribute) JOptionPane.showInputDialog( (Component) e.getSource(), - "Select the index to insert this attribute at.", - "Attribute Index", + "Select the attribute to remove.", + "Select Attribute", JOptionPane.PLAIN_MESSAGE, null, r.getAttributes().toArray(new Attribute[0]), diff --git a/src/main/java/nl/andrewlalis/erme/model/Attribute.java b/src/main/java/nl/andrewlalis/erme/model/Attribute.java index d35fb17..7edf049 100644 --- a/src/main/java/nl/andrewlalis/erme/model/Attribute.java +++ b/src/main/java/nl/andrewlalis/erme/model/Attribute.java @@ -46,6 +46,7 @@ public class Attribute implements Serializable { if (o == null || getClass() != o.getClass()) return false; Attribute attribute = (Attribute) o; return type == attribute.type && + relation.equals(attribute.getRelation()) && name.equals(attribute.name); } @@ -56,6 +57,6 @@ public class Attribute implements Serializable { @Override public String toString() { - return this.getName() + ":" + this.getType().name(); + return this.getName(); } } diff --git a/src/main/java/nl/andrewlalis/erme/model/AttributeType.java b/src/main/java/nl/andrewlalis/erme/model/AttributeType.java index c9bab76..42f667f 100644 --- a/src/main/java/nl/andrewlalis/erme/model/AttributeType.java +++ b/src/main/java/nl/andrewlalis/erme/model/AttributeType.java @@ -3,6 +3,5 @@ package nl.andrewlalis.erme.model; public enum AttributeType { PLAIN, ID_KEY, - PARTIAL_ID_KEY, - FOREIGN_KEY + PARTIAL_ID_KEY } diff --git a/src/main/java/nl/andrewlalis/erme/model/ForeignKeyAttribute.java b/src/main/java/nl/andrewlalis/erme/model/ForeignKeyAttribute.java index 1ccb2d0..00c72b2 100644 --- a/src/main/java/nl/andrewlalis/erme/model/ForeignKeyAttribute.java +++ b/src/main/java/nl/andrewlalis/erme/model/ForeignKeyAttribute.java @@ -6,13 +6,13 @@ import lombok.Getter; public class ForeignKeyAttribute extends Attribute { private Attribute reference; - public ForeignKeyAttribute(Relation relation, String name, Attribute reference) { - super(relation, AttributeType.FOREIGN_KEY, name); + public ForeignKeyAttribute(Relation relation, AttributeType type, String name, Attribute reference) { + super(relation, type, name); this.reference = reference; } - public ForeignKeyAttribute(Relation relation, String name, String referencedRelationName, String referencedAttributeName) { - this(relation, name, relation.getModel().findAttribute(referencedRelationName, referencedAttributeName)); + public ForeignKeyAttribute(Relation relation, AttributeType type, String name, String referencedRelationName, String referencedAttributeName) { + this(relation, type, name, relation.getModel().findAttribute(referencedRelationName, referencedAttributeName)); if (this.getReference() == null) { throw new IllegalArgumentException("Unknown attribute name."); } @@ -22,4 +22,13 @@ public class ForeignKeyAttribute extends Attribute { this.reference = reference; this.getRelation().getModel().fireChangedEvent(); } + + public String getFullReferenceName() { + return this.getReference().getRelation().getName() + "." + this.getReference().getName(); + } + + @Override + public String toString() { + return super.toString() + "->" + this.getFullReferenceName(); + } } diff --git a/src/main/java/nl/andrewlalis/erme/model/MappingModel.java b/src/main/java/nl/andrewlalis/erme/model/MappingModel.java index 9275243..e576346 100644 --- a/src/main/java/nl/andrewlalis/erme/model/MappingModel.java +++ b/src/main/java/nl/andrewlalis/erme/model/MappingModel.java @@ -52,6 +52,21 @@ public class MappingModel implements Serializable { return null; } + public void removeAllReferencingAttributes(Attribute referenced) { + for (Relation r : this.getRelations()) { + Set removalSet = new HashSet<>(); + for (Attribute a : r.getAttributes()) { + if (a instanceof ForeignKeyAttribute) { + ForeignKeyAttribute fkA = (ForeignKeyAttribute) a; + if (fkA.getReference().equals(referenced)) { + removalSet.add(fkA); + } + } + } + removalSet.forEach(r::removeAttribute); + } + } + 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 f082d52..4722c2e 100644 --- a/src/main/java/nl/andrewlalis/erme/model/Relation.java +++ b/src/main/java/nl/andrewlalis/erme/model/Relation.java @@ -54,6 +54,7 @@ public class Relation implements Serializable { public void removeAttribute(Attribute attribute) { if (this.attributes.remove(attribute)) { + this.model.removeAllReferencingAttributes(attribute); this.model.fireChangedEvent(); } } @@ -83,4 +84,9 @@ public class Relation implements Serializable { public int hashCode() { return this.getName().hashCode(); } + + @Override + public String toString() { + return this.getName(); + } } diff --git a/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java b/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java index 4fc20bf..7d63ce4 100644 --- a/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java +++ b/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java @@ -3,6 +3,7 @@ package nl.andrewlalis.erme.view; import lombok.Getter; import nl.andrewlalis.erme.control.actions.ExportToImageAction; import nl.andrewlalis.erme.control.actions.LoadAction; +import nl.andrewlalis.erme.control.actions.NewModelAction; import nl.andrewlalis.erme.control.actions.SaveAction; import nl.andrewlalis.erme.control.actions.edits.AddAttributeAction; import nl.andrewlalis.erme.control.actions.edits.AddRelationAction; @@ -74,6 +75,7 @@ public class DiagramPanel extends JPanel implements ModelChangeListener { * Updates all the action singletons with the latest model information. */ private void updateActionModels() { + NewModelAction.getInstance().setDiagramPanel(this); SaveAction.getInstance().setModel(this.model); LoadAction.getInstance().setDiagramPanel(this); ExportToImageAction.getInstance().setModel(this.model); diff --git a/src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java b/src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java index e98276c..4a797f0 100644 --- a/src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java +++ b/src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java @@ -16,7 +16,6 @@ public class DiagramPopupMenu extends JPopupMenu { List selectedRelations = model.getSelectedRelations(); if (selectedRelations.size() == 0) { this.add(AddRelationAction.getInstance()); - this.add(ExportToImageAction.getInstance()); } if (selectedRelations.size() > 0) { this.add(RemoveRelationAction.getInstance()); diff --git a/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java b/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java index 269aa5c..b37b36a 100644 --- a/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java +++ b/src/main/java/nl/andrewlalis/erme/view/EditorFrame.java @@ -21,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 ForeignKeyAttribute(tokensRelation,"username", "Users", "username")); + tokensRelation.addAttribute(new ForeignKeyAttribute(tokensRelation, AttributeType.PLAIN, "username", "Users", "username")); tokensRelation.addAttribute(new Attribute(tokensRelation, AttributeType.PLAIN, "expirationDate")); model.addRelation(tokensRelation); diff --git a/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java b/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java index 8ed10d0..5d5d67c 100644 --- a/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java +++ b/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java @@ -15,12 +15,15 @@ public class EditorMenuBar extends JMenuBar { public EditorMenuBar() { this.add(this.buildFileMenu()); this.add(this.buildEditMenu()); + this.add(this.buildHelpMenu()); } private JMenu buildFileMenu() { JMenu menu = new JMenu("File"); + menu.add(NewModelAction.getInstance()); menu.add(SaveAction.getInstance()); menu.add(LoadAction.getInstance()); + menu.addSeparator(); menu.add(ExportToImageAction.getInstance()); menu.addSeparator(); menu.add(ExitAction.getInstance()); @@ -38,4 +41,10 @@ public class EditorMenuBar extends JMenuBar { menu.add(RedoAction.getInstance()); return menu; } + + private JMenu buildHelpMenu() { + JMenu menu = new JMenu("Help"); + menu.add("About"); + return menu; + } } diff --git a/src/main/java/nl/andrewlalis/erme/view/view_models/AttributeViewModel.java b/src/main/java/nl/andrewlalis/erme/view/view_models/AttributeViewModel.java index ee44f3d..e58a692 100644 --- a/src/main/java/nl/andrewlalis/erme/view/view_models/AttributeViewModel.java +++ b/src/main/java/nl/andrewlalis/erme/view/view_models/AttributeViewModel.java @@ -2,6 +2,7 @@ package nl.andrewlalis.erme.view.view_models; import nl.andrewlalis.erme.model.Attribute; import nl.andrewlalis.erme.model.AttributeType; +import nl.andrewlalis.erme.model.ForeignKeyAttribute; import java.awt.*; import java.awt.font.TextAttribute; @@ -13,6 +14,7 @@ public class AttributeViewModel implements ViewModel { public static final int PADDING_Y = 5; public static final Color BACKGROUND_COLOR = Color.LIGHT_GRAY; public static final Color FONT_COLOR = Color.BLACK; + public static final float FK_FONT_SIZE = 11.0f; private final Attribute attribute; @@ -23,33 +25,45 @@ public class AttributeViewModel implements ViewModel { @Override public void draw(Graphics2D g) { AttributedString as = this.getAttributedString(g); - Rectangle r = this.getBounds(g, as); + Rectangle r = this.getBoxBounds(g, as); g.setColor(BACKGROUND_COLOR); g.fillRect(r.x, r.y, r.width, r.height); g.setColor(FONT_COLOR); g.drawRect(r.x, r.y, r.width, r.height); g.drawString(as.getIterator(), r.x + PADDING_X, r.y + (r.height - PADDING_Y)); + if (this.attribute instanceof ForeignKeyAttribute) { + ForeignKeyAttribute fkAttribute = (ForeignKeyAttribute) this.attribute; + Font originalFont = g.getFont(); + g.setFont(g.getFont().deriveFont(Font.ITALIC, FK_FONT_SIZE)); + g.drawString(fkAttribute.getFullReferenceName(), r.x + PADDING_X, r.y - PADDING_Y); + g.setFont(originalFont); + } } - private Rectangle getBounds(Graphics2D g, AttributedString as) { + private Rectangle getBoxBounds(Graphics2D g, AttributedString as) { int x = this.attribute.getRelation().getPosition().x + RelationViewModel.PADDING_X; - int y = this.attribute.getRelation().getPosition().y + this.attribute.getRelation().getViewModel().getNameBounds(g).height + PADDING_Y; + int y = this.attribute.getRelation().getPosition().y + this.attribute.getRelation().getViewModel().getNameBounds(g).height + RelationViewModel.ATTRIBUTE_SEPARATION; int i = 0; while (!this.attribute.getRelation().getAttributes().get(i).equals(this.attribute)) { - x += g.getFontMetrics().stringWidth(this.attribute.getRelation().getAttributes().get(i).getName()) + (2 * PADDING_X); + x += this.attribute.getRelation().getAttributes().get(i).getViewModel().getBoxBounds(g).width; i++; } - Rectangle2D rect = g.getFontMetrics().getStringBounds(as.getIterator(), 0, this.attribute.getName().length(), g); - return new Rectangle( - x, - y, - (int) rect.getWidth() + (2 * PADDING_X), - (int) rect.getHeight() + (2 * PADDING_Y) - ); + Rectangle2D nameRect = g.getFontMetrics().getStringBounds(as.getIterator(), 0, this.attribute.getName().length(), g); + int width = (int) nameRect.getWidth() + (2 * PADDING_X); + int height = (int) nameRect.getHeight() + (2 * PADDING_Y); + if (this.attribute instanceof ForeignKeyAttribute) { + ForeignKeyAttribute fkAttribute = (ForeignKeyAttribute) this.attribute; + Font originalFont = g.getFont(); + g.setFont(g.getFont().deriveFont(Font.ITALIC, FK_FONT_SIZE)); + Rectangle referenceNameBounds = g.getFontMetrics().getStringBounds(fkAttribute.getFullReferenceName(), g).getBounds(); + g.setFont(originalFont); + width = Math.max(width, referenceNameBounds.width + (2 * PADDING_X)); + } + return new Rectangle(x, y, width, height); } - public Rectangle getBounds(Graphics2D g) { - return this.getBounds(g, this.getAttributedString(g)); + public Rectangle getBoxBounds(Graphics2D g) { + return this.getBoxBounds(g, this.getAttributedString(g)); } private AttributedString getAttributedString(Graphics2D g) { 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 a4317e0..21c02c4 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 @@ -11,6 +11,7 @@ import java.text.AttributedString; public class RelationViewModel implements ViewModel { public static final int PADDING_X = 5; public static final int PADDING_Y = 5; + public static final int ATTRIBUTE_SEPARATION = 10; private final Relation relation; @@ -40,13 +41,13 @@ public class RelationViewModel implements ViewModel { int totalAttributeWidth = 0; int maxAttributeHeight = 0; for (Attribute a : this.relation.getAttributes()) { - Rectangle attributeBounds = a.getViewModel().getBounds(g); + Rectangle attributeBounds = a.getViewModel().getBoxBounds(g); totalAttributeWidth += attributeBounds.width; maxAttributeHeight = Math.max(maxAttributeHeight, attributeBounds.height); } Rectangle nameBounds = this.getNameBounds(g); rect.width = Math.max(totalAttributeWidth, nameBounds.width) + (2 * PADDING_X); - rect.height = nameBounds.height + maxAttributeHeight + (2 * PADDING_Y); + rect.height = nameBounds.height + maxAttributeHeight + (2 * PADDING_Y) + ATTRIBUTE_SEPARATION; return rect; }