You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by ab...@apache.org on 2019/03/06 14:54:37 UTC

[cayenne] branch master updated: CAY-2542 Redesign ObjRelationship editor dialog

This is an automated email from the ASF dual-hosted git repository.

abulatski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new 961cbe1  CAY-2542 Redesign ObjRelationship editor dialog
961cbe1 is described below

commit 961cbe1aae496d66519a05e4aaaf8ec0084b0525
Author: Arseni Bulatski <an...@gmail.com>
AuthorDate: Wed Mar 6 17:49:23 2019 +0300

    CAY-2542 Redesign ObjRelationship editor dialog
---
 RELEASE-NOTES.txt                                  |   1 +
 .../modeler/action/CreateRelationshipAction.java   |  11 +-
 .../dialog/objentity/ObjRelationshipInfo.java      | 253 +++++++++++++--------
 .../dialog/objentity/ObjRelationshipInfoView.java  | 150 +++++++-----
 .../modeler/editor/ObjEntityRelationshipPanel.java |  60 ++---
 .../modeler/undo/RelationshipUndoableEdit.java     |  59 ++++-
 6 files changed, 332 insertions(+), 202 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 4d28fa0..4f1f186 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -23,6 +23,7 @@ CAY-2518 Add method to append having qualifier expression to ObjectSelect
 CAY-2520 Split ObjectId into several specialized variants
 CAY-2522 Make ObjectSelect a direct query
 CAY-2540 Modeler: redesign dbRelationship editor dialog
+CAY-2542 Redesign ObjRelationship editor dialog
 CAY-2543 Move ResultSetMapping generation from metadata to translator
 
 Bug Fixes:
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateRelationshipAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateRelationshipAction.java
index 9c74e42..325a44d 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateRelationshipAction.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateRelationshipAction.java
@@ -23,7 +23,6 @@ import java.awt.event.ActionEvent;
 
 import org.apache.cayenne.configuration.ConfigurationNode;
 import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.dbsync.naming.NameBuilder;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.Entity;
@@ -35,8 +34,8 @@ import org.apache.cayenne.map.event.RelationshipEvent;
 import org.apache.cayenne.modeler.Application;
 import org.apache.cayenne.modeler.ProjectController;
 import org.apache.cayenne.modeler.dialog.DbRelationshipDialog;
+import org.apache.cayenne.modeler.dialog.objentity.ObjRelationshipInfo;
 import org.apache.cayenne.modeler.event.RelationshipDisplayEvent;
-import org.apache.cayenne.modeler.undo.CreateRelationshipUndoableEdit;
 import org.apache.cayenne.modeler.util.CayenneAction;
 import org.apache.cayenne.util.DeleteRuleUpdater;
 
@@ -93,12 +92,10 @@ public class CreateRelationshipAction extends CayenneAction {
         ObjEntity objEnt = getProjectController().getCurrentObjEntity();
         if (objEnt != null) {
 
-            ObjRelationship rel = new ObjRelationship();
-            rel.setName(NameBuilder.builder(rel, objEnt).name());
-            createObjRelationship(objEnt, rel);
+            new ObjRelationshipInfo(getProjectController())
+                    .createRelationship(objEnt)
+                    .startupAction();
 
-            application.getUndoManager().addEdit(
-                    new CreateRelationshipUndoableEdit(objEnt, new ObjRelationship[]{rel}));
         } else {
             DbEntity dbEnt = getProjectController().getCurrentDbEntity();
             if (dbEnt != null) {
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfo.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfo.java
index c3c3062..53f8e34 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfo.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfo.java
@@ -32,48 +32,59 @@ import java.util.Objects;
 import java.util.Optional;
 
 import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.dbsync.naming.NameBuilder;
 import org.apache.cayenne.dbsync.naming.ObjectNameGenerator;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.DeleteRule;
 import org.apache.cayenne.map.Entity;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
 import org.apache.cayenne.map.Relationship;
+import org.apache.cayenne.map.event.MapEvent;
 import org.apache.cayenne.map.event.RelationshipEvent;
 import org.apache.cayenne.modeler.Application;
 import org.apache.cayenne.modeler.ClassLoadingService;
 import org.apache.cayenne.modeler.ProjectController;
 import org.apache.cayenne.modeler.dialog.DbRelationshipDialog;
+import org.apache.cayenne.modeler.event.RelationshipDisplayEvent;
+import org.apache.cayenne.modeler.undo.CreateRelationshipUndoableEdit;
+import org.apache.cayenne.modeler.undo.RelationshipUndoableEdit;
 import org.apache.cayenne.modeler.util.CayenneController;
 import org.apache.cayenne.modeler.util.Comparators;
 import org.apache.cayenne.modeler.util.EntityTreeModel;
 import org.apache.cayenne.modeler.util.EntityTreeRelationshipFilter;
 import org.apache.cayenne.modeler.util.MultiColumnBrowser;
+import org.apache.cayenne.project.extension.info.ObjectInfo;
 import org.apache.cayenne.util.DeleteRuleUpdater;
 import org.apache.cayenne.util.Util;
 
 public class ObjRelationshipInfo extends CayenneController implements TreeSelectionListener {
 
-    static final String COLLECTION_TYPE_MAP = "java.util.Map";
-    static final String COLLECTION_TYPE_SET = "java.util.Set";
-    static final String COLLECTION_TYPE_COLLECTION = "java.util.Collection";
-    static final String DEFAULT_MAP_KEY = "ID (default)";
+    private static final String COLLECTION_TYPE_MAP = "java.util.Map";
+    private static final String COLLECTION_TYPE_SET = "java.util.Set";
+    private static final String COLLECTION_TYPE_COLLECTION = "java.util.Collection";
+    private static final String DEFAULT_MAP_KEY = "ID (default)";
 
     protected ObjRelationship relationship;
 
-    protected List<DbRelationship> dbRelationships;
+    private List<DbRelationship> dbRelationships;
 
-    protected List<DbRelationship> savedDbRelationships;
-    protected ObjEntity objectTarget;
-    protected List<ObjEntity> objectTargets;
-    protected List<String> targetCollections;
-    protected List<String> mapKeys;
-    protected String targetCollection;
-    protected String mapKey;
-    protected ObjRelationshipInfoView view;
-    protected String currentPath;
-    protected ProjectController mediator;
+    private List<DbRelationship> savedDbRelationships;
+    private ObjEntity objectTarget;
+    private List<ObjEntity> objectTargets;
+    private List<String> targetCollections;
+    private List<String> mapKeys;
+    private String targetCollection;
+    private String mapKey;
+    private ObjRelationshipInfoView view;
+    private String currentPath;
+    private ProjectController mediator;
+
+    private RelationshipUndoableEdit undo;
+    private boolean isCreate = false;
 
     /**
      * Starts options dialog.
@@ -87,30 +98,11 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
         view.setVisible(true);
     }
 
-    public ObjRelationshipInfo(ProjectController mediator, ObjRelationship relationship) {
+    public ObjRelationshipInfo(ProjectController mediator) {
         super(mediator);
-        this.view = new ObjRelationshipInfoView(mediator);
+        this.view = new ObjRelationshipInfoView();
         this.mediator = mediator;
         getPathBrowser().addTreeSelectionListener(this);
-        view.sourceEntityLabel.setText(relationship.getSourceEntity().getName());
-        this.relationship = relationship;
-        this.view.getRelationshipName().setText(relationship.getName());
-        this.mapKey = relationship.getMapKey();
-        this.targetCollection = relationship.getCollectionType();
-        if (targetCollection == null) {
-            targetCollection = ObjRelationship.DEFAULT_COLLECTION_TYPE;
-        }
-
-        this.objectTarget = relationship.getTargetEntity();
-        if (objectTarget != null) {
-            updateTargetCombo(objectTarget.getDbEntity());
-            view.targetCombo.setSelectedItem(objectTarget.getName());
-        }
-
-        // validate -
-        // current limitation is that an ObjRelationship must have source
-        // and target entities present, with DbEntities chosen.
-        validateCanMap();
 
         this.targetCollections = new ArrayList<>(4);
         targetCollections.add(COLLECTION_TYPE_COLLECTION);
@@ -119,28 +111,38 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
         targetCollections.add(COLLECTION_TYPE_SET);
 
         for (String s : targetCollections) {
-            view.collectionTypeCombo.addItem(s);
+            view.getCollectionTypeCombo().addItem(s);
         }
 
         this.mapKeys = new ArrayList<>();
-        initMapKeys();
+    }
 
-        // setup path
-        dbRelationships = new ArrayList<>(relationship.getDbRelationships());
-        selectPath();
-        updateCollectionChoosers();
+    public ObjRelationshipInfo createRelationship(ObjEntity objEntity) {
+        ObjRelationship rel = new ObjRelationship();
+        rel.setName(NameBuilder.builder(rel, objEntity).name());
+        rel.setSourceEntity(objEntity);
+        DeleteRuleUpdater.updateObjRelationship(rel);
+        isCreate = true;
+        return modifyRelationship(rel);
+    }
+
+    public ObjRelationshipInfo modifyRelationship(ObjRelationship rel) {
+        this.relationship = rel;
+        this.undo = new RelationshipUndoableEdit(rel);
+        // validate -
+        // current limitation is that an ObjRelationship must have source
+        // and target entities present, with DbEntities chosen.
+        validateCanMap();
 
-        // add dummy last relationship if we are not connected
-        connectEnds();
         initFromModel();
         initController();
+        return this;
     }
 
     private void initController() {
         view.getCancelButton().addActionListener(e -> view.dispose());
         view.getSaveButton().addActionListener(e -> saveMapping());
         view.getNewRelButton().addActionListener(e -> createRelationship());
-        view.getSelectPathButton().addActionListener(e -> selectPath());
         view.getCollectionTypeCombo().addActionListener(e -> setCollectionType());
         view.getMapKeysCombo().addActionListener(e -> setMapKey());
         view.getTargetCombo().addItemListener(e -> {
@@ -154,47 +156,79 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
                 }
             }
         });
+        view.getDeleteRule().addActionListener(e -> setDeleteRule());
+        view.getUsedForLocking().addActionListener(e -> setUsedForLocking());
+        view.getComment().addActionListener(e -> setComment());
     }
 
-    void initFromModel() {
+    private void initFromModel() {
+        view.getSourceEntityLabel().setText(relationship.getSourceEntity().getName());
+        this.view.getRelationshipName().setText(relationship.getName());
+        this.mapKey = relationship.getMapKey();
+        this.targetCollection = relationship.getCollectionType();
+        if (targetCollection == null) {
+            targetCollection = ObjRelationship.DEFAULT_COLLECTION_TYPE;
+        }
+        this.objectTarget = relationship.getTargetEntity();
+        if (objectTarget != null) {
+            updateTargetCombo(objectTarget.getDbEntity());
+            view.getTargetCombo().setSelectedItem(objectTarget.getName());
+        }
+        view.getUsedForLocking().setSelected(relationship.isUsedForLocking());
+        view.getDeleteRule().setSelectedItem(DeleteRule.deleteRuleName(relationship.getDeleteRule()));
+        view.getComment().setText(
+                ObjectInfo.getFromMetaData(mediator.getApplication().getMetaData(),
+                        relationship,
+                        ObjectInfo.COMMENT));
+
+        setSemantics();
+        // setup path
+        dbRelationships = new ArrayList<>(relationship.getDbRelationships());
+        this.savedDbRelationships = dbRelationships;
+        initMapKeys();
+        updateCollectionChoosers();
+        // add dummy last relationship if we are not connected
+        connectEnds();
 
-        if (view.pathBrowser.getModel() == null) {
+        if (view.getPathBrowser().getModel() == null) {
             EntityTreeModel treeModel = new EntityTreeModel(getStartEntity());
             treeModel.setFilter(new EntityTreeRelationshipFilter());
 
-            view.pathBrowser.setModel(treeModel);
+            view.getPathBrowser().setModel(treeModel);
 
             setSelectionPath(getSavedDbRelationships());
         }
+
+        view.getSaveButton().setEnabled(!this.dbRelationships.isEmpty());
     }
 
     /**
      * Selects path in browser
      */
-    void setSelectionPath(List<DbRelationship> rels) {
+    private void setSelectionPath(List<DbRelationship> rels) {
         Object[] path = new Object[rels.size() + 1];
         path[0] = getStartEntity();
 
         System.arraycopy(rels.toArray(), 0, path, 1, rels.size());
 
-        view.pathBrowser.setSelectionPath(new TreePath(path));
+        view.getPathBrowser().setSelectionPath(new TreePath(path));
     }
 
-    public void setCollectionType() {
-        setTargetCollection((String) view.collectionTypeCombo.getSelectedItem());
+    private void setCollectionType() {
+        setTargetCollection((String) view.getCollectionTypeCombo().getSelectedItem());
 
         if (COLLECTION_TYPE_MAP.equals(targetCollection)) {
-            view.mapKeysLabel.setEnabled(true);
-            view.mapKeysCombo.setEnabled(true);
+            view.getMapKeysLabel().setEnabled(true);
+            view.getMapKeysCombo().setEnabled(true);
             setMapKey();
         } else {
-            view.mapKeysLabel.setEnabled(false);
-            view.mapKeysCombo.setEnabled(false);
+            view.getMapKeysLabel().setEnabled(false);
+            view.getMapKeysCombo().setEnabled(false);
         }
     }
 
     public void setMapKey() {
-        setMapKey((String) view.mapKeysCombo.getSelectedItem());
+        setMapKey((String) view.getMapKeysCombo().getSelectedItem());
     }
 
     @Override
@@ -202,26 +236,6 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
         return view;
     }
 
-    public void setSavedDbRelationships(List<DbRelationship> rels) {
-        this.savedDbRelationships = rels;
-
-        String currPath = "";
-        for (DbRelationship rel : rels) {
-            currPath += "->" + rel.getName();
-        }
-
-        if (rels.size() > 0) {
-            currPath = currPath.substring(2);
-        }
-
-        currentPath = currPath;
-        view.currentPathLabel.setText(currPath);
-    }
-
-    public void selectPath() {
-        setSavedDbRelationships(new ArrayList<>(dbRelationships));
-    }
-
     /**
      * Reverts current path to saved path
      */
@@ -235,18 +249,18 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
      */
     protected void updateCollectionChoosers() {
         boolean collectionTypeEnabled = isToMany();
-        view.collectionTypeCombo.setEnabled(collectionTypeEnabled);
-        view.collectionTypeLabel.setEnabled(collectionTypeEnabled);
+        view.getCollectionTypeCombo().setEnabled(collectionTypeEnabled);
+        view.getCollectionTypeLabel().setEnabled(collectionTypeEnabled);
         if (collectionTypeEnabled) {
-            view.collectionTypeCombo.setSelectedItem(targetCollection);
+            view.getCollectionTypeCombo().setSelectedItem(targetCollection);
         }
 
         boolean mapKeysEnabled = collectionTypeEnabled
-                && ObjRelationshipInfo.COLLECTION_TYPE_MAP.equals(view.collectionTypeCombo.getSelectedItem());
-        view.mapKeysCombo.setEnabled(mapKeysEnabled);
-        view.mapKeysLabel.setEnabled(mapKeysEnabled);
+                && ObjRelationshipInfo.COLLECTION_TYPE_MAP.equals(view.getCollectionTypeCombo().getSelectedItem());
+        view.getMapKeysCombo().setEnabled(mapKeysEnabled);
+        view.getMapKeysLabel().setEnabled(mapKeysEnabled);
         if (mapKeysEnabled) {
-            view.mapKeysCombo.setSelectedItem(mapKey);
+            view.getMapKeysCombo().setSelectedItem(mapKey);
         }
     }
 
@@ -260,26 +274,43 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
 
     protected void saveMapping() {
         if (!getDbRelationships().equals(getSavedDbRelationships())) {
-            if (JOptionPane.showConfirmDialog(getView(),
+            if (getSavedDbRelationships().isEmpty() || JOptionPane.showConfirmDialog(getView(),
                     "You have changed Db Relationship path. Do you want it to be saved?", "Save ObjRelationship",
                     JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
-                selectPath();
+                this.savedDbRelationships = new ArrayList<>(dbRelationships);
             }
         }
 
-        if (savePath()) {
-            mediator.fireObjRelationshipEvent(new RelationshipEvent(Application.getFrame(), getRelationship(),
-                    getRelationship().getSourceEntity()));
+        savePath();
+        relationship.getSourceEntity().addRelationship(relationship);
+        if(isCreate) {
+            fireObjRelationshipEvent(this);
+            Application.getInstance().getUndoManager().addEdit(
+                    new CreateRelationshipUndoableEdit(relationship.getSourceEntity(), new ObjRelationship[]{relationship}));
+        } else {
+            mediator.fireObjRelationshipEvent(new RelationshipEvent(this, relationship,
+                    relationship.getSourceEntity(), MapEvent.CHANGE));
+            Application.getInstance().getUndoManager().addEdit(undo);
         }
-        view.sourceEntityLabel.setText(relationship.getSourceEntity().getName());
+
+        view.getSourceEntityLabel().setText(relationship.getSourceEntity().getName());
         view.dispose();
     }
 
+    private void fireObjRelationshipEvent(Object src) {
+        mediator.fireObjRelationshipEvent(new RelationshipEvent(src, relationship, relationship.getSourceEntity(), MapEvent.ADD));
+
+        RelationshipDisplayEvent rde = new RelationshipDisplayEvent(src, relationship, relationship.getSourceEntity(), mediator.getCurrentDataMap(),
+                (DataChannelDescriptor) mediator.getProject().getRootNode());
+
+        mediator.fireObjRelationshipDisplayEvent(rde);
+    }
+
     /**
      * @return relationship path browser
      */
     public MultiColumnBrowser getPathBrowser() {
-        return view.pathBrowser;
+        return view.getPathBrowser();
     }
 
     /**
@@ -330,6 +361,31 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
         }
     }
 
+    private void setDeleteRule() {
+        relationship.setDeleteRule(DeleteRule.deleteRuleForName(
+                String.valueOf(view.getDeleteRule().getSelectedItem())));
+    }
+
+    private void setUsedForLocking() {
+        relationship.setUsedForLocking(view.getUsedForLocking().isSelected());
+    }
+
+    private void setComment() {
+        ObjectInfo.putToMetaData(mediator.getApplication().getMetaData(),
+                relationship,
+                ObjectInfo.COMMENT,
+                view.getComment().getText());
+    }
+
+    private void setSemantics() {
+        StringBuilder semantics =  new StringBuilder(20);
+        semantics.append(relationship.isToMany() ? "to many" : "to one");
+        if (relationship.isReadOnly()) {
+            semantics.append(", read-only");
+        }
+        view.getSemanticsLabel().setText(semantics.toString());
+    }
+
     /**
      * Sets list of DB Relationships current ObjRelationship is mapped to
      */
@@ -373,13 +429,13 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
         for (ObjAttribute attribute : this.objectTarget.getAttributes()) {
             mapKeys.add(attribute.getName());
         }
-        view.mapKeysCombo.removeAllItems();
+        view.getMapKeysCombo().removeAllItems();
         for (String s : mapKeys)
-            view.mapKeysCombo.addItem(s);
+            view.getMapKeysCombo().addItem(s);
 
         if (mapKey != null && !mapKeys.contains(mapKey)) {
             mapKey = DEFAULT_MAP_KEY;
-            view.mapKeysCombo.setSelectedItem(mapKey);
+            view.getMapKeysCombo().setSelectedItem(mapKey);
         }
     }
 
@@ -396,16 +452,12 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
             objectTargets.addAll(dbTarget.getDataMap().getMappedEntities(dbTarget));
             objectTargets.sort(Comparators.getNamedObjectComparator());
         }
-        view.targetCombo.removeAllItems();
+        view.getTargetCombo().removeAllItems();
         for (ObjEntity s : objectTargets) {
-            view.targetCombo.addItem(s.getName());
+            view.getTargetCombo().addItem(s.getName());
         }
     }
 
-    public ObjRelationship getRelationship() {
-        return relationship;
-    }
-
     /**
      * @return list of DB Relationships current ObjRelationship is mapped to
      */
@@ -433,6 +485,7 @@ public class ObjRelationshipInfo extends CayenneController implements TreeSelect
      */
     public void setDbRelationships(List<DbRelationship> rels) {
         this.dbRelationships = rels;
+        view.getSaveButton().setEnabled(true);
 
         updateTargetCombo(rels.size() > 0 ? rels.get(rels.size() - 1).getTargetEntity() : null);
         updateCollectionChoosers();
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfoView.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfoView.java
index 96fb522..bc3d984 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfoView.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/objentity/ObjRelationshipInfoView.java
@@ -18,17 +18,8 @@
  ****************************************************************/
 package org.apache.cayenne.modeler.dialog.objentity;
 
-import com.jgoodies.forms.builder.PanelBuilder;
-import com.jgoodies.forms.layout.CellConstraints;
-import com.jgoodies.forms.layout.FormLayout;
-import org.apache.cayenne.modeler.Application;
-import org.apache.cayenne.modeler.ProjectController;
-import org.apache.cayenne.modeler.util.DefaultWidgetFactory;
-import org.apache.cayenne.modeler.util.MultiColumnBrowser;
-import org.apache.cayenne.modeler.util.PanelFactory;
-import org.apache.cayenne.modeler.util.WidgetFactory;
-
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JDialog;
 import javax.swing.JLabel;
@@ -40,45 +31,58 @@ import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
 
+import com.jgoodies.forms.builder.PanelBuilder;
+import com.jgoodies.forms.layout.CellConstraints;
+import com.jgoodies.forms.layout.FormLayout;
+import org.apache.cayenne.map.DeleteRule;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.util.DefaultWidgetFactory;
+import org.apache.cayenne.modeler.util.MultiColumnBrowser;
+import org.apache.cayenne.modeler.util.PanelFactory;
+import org.apache.cayenne.modeler.util.WidgetFactory;
+
 public class ObjRelationshipInfoView extends JDialog{
     
-    static final Dimension BROWSER_CELL_DIM = new Dimension(130, 200);
+    private static final Dimension BROWSER_CELL_DIM = new Dimension(130, 200);
+
+    private static final Object[] DELETE_RULES = new Object[]{
+            DeleteRule.deleteRuleName(DeleteRule.NO_ACTION),
+            DeleteRule.deleteRuleName(DeleteRule.NULLIFY),
+            DeleteRule.deleteRuleName(DeleteRule.CASCADE),
+            DeleteRule.deleteRuleName(DeleteRule.DENY),
+    };
     
-    protected MultiColumnBrowser pathBrowser;
-    
-    protected WidgetFactory widgetFactory;
+    private MultiColumnBrowser pathBrowser;
 
-    protected Component collectionTypeLabel;
-    protected JComboBox<String> collectionTypeCombo;
-    protected Component mapKeysLabel;
-    protected JComboBox<String> mapKeysCombo;
+    private Component collectionTypeLabel;
+    private JComboBox<String> collectionTypeCombo;
+    private Component mapKeysLabel;
+    private JComboBox<String> mapKeysCombo;
 
-    protected JButton saveButton;
-    protected JButton cancelButton;
-    protected JButton newRelButton;
-    protected JButton selectPathButton;
-    
-    protected JTextField relationshipName;
-    protected JLabel currentPathLabel;
-    protected JLabel sourceEntityLabel;
-    protected JComboBox<String> targetCombo;
-    
-    ProjectController mediator;
+    private JButton saveButton;
+    private JButton cancelButton;
+    private JButton newRelButton;
     
-    public ObjRelationshipInfoView(final ProjectController mediator) {
+    private JTextField relationshipName;
+    private JLabel semanticsLabel;
+    private JLabel sourceEntityLabel;
+    private JComboBox<String> targetCombo;
+
+    private JComboBox deleteRule;
+    private JCheckBox usedForLocking;
+    private JTextField comment;
+
+    public ObjRelationshipInfoView() {
         super(Application.getFrame());
 
-        this.mediator = mediator;
-        
-        this.widgetFactory = new DefaultWidgetFactory();
+        WidgetFactory widgetFactory = new DefaultWidgetFactory();
         
         this.cancelButton = new JButton("Cancel");
         this.saveButton = new JButton("Done");
         this.newRelButton = new JButton("New DbRelationship");
-        this.selectPathButton = new JButton("Select Path");
-        this.relationshipName= new JTextField(25);
-        this.currentPathLabel=new JLabel();
-        this.sourceEntityLabel=new JLabel();
+        this.relationshipName = new JTextField(25);
+        this.semanticsLabel = new JLabel();
+        this.sourceEntityLabel = new JLabel();
         
         cancelButton.setEnabled(true);
         getRootPane().setDefaultButton(saveButton);
@@ -89,13 +93,19 @@ public class ObjRelationshipInfoView extends JDialog{
         this.targetCombo = widgetFactory.createComboBox();
         targetCombo.setVisible(true);
         
-        this.mapKeysCombo  = widgetFactory.createComboBox();
+        this.mapKeysCombo = widgetFactory.createComboBox();
         mapKeysCombo.setVisible(true);
       
         
         pathBrowser = new ObjRelationshipPathBrowser();
         pathBrowser.setPreferredColumnSize(BROWSER_CELL_DIM);
         pathBrowser.setDefaultRenderer();
+
+        this.deleteRule = Application.getWidgetFactory().createComboBox(
+                DELETE_RULES,
+                false);
+        this.usedForLocking = new JCheckBox();
+        this.comment = new JTextField();
         
         setTitle("ObjRelationship Inspector");
         setLayout(new BorderLayout());
@@ -103,7 +113,7 @@ public class ObjRelationshipInfoView extends JDialog{
         PanelBuilder builder = new PanelBuilder(
                 new FormLayout(
                         "right:max(50dlu;pref), 3dlu, fill:min(150dlu;pref), 3dlu, 300dlu, 3dlu, fill:min(120dlu;pref)",
-                        "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, top:14dlu, 3dlu, top:p:grow"));
+                        "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, top:14dlu, 3dlu, top:p:grow"));
         builder.setDefaultDialogBorder();
 
         builder.addSeparator("ObjRelationship Information", cc.xywh(1, 1, 5, 1));
@@ -117,8 +127,8 @@ public class ObjRelationshipInfoView extends JDialog{
         builder.addLabel("Relationship Name:", cc.xy(1, 7));
         builder.add(relationshipName, cc.xywh(3, 7, 1, 1));
 
-        builder.addLabel("Current Db Path:", cc.xy(1, 9));
-        builder.add(currentPathLabel, cc.xywh(3, 9, 5, 1));
+        builder.addLabel("Semantics:", cc.xy(1, 9));
+        builder.add(semanticsLabel, cc.xywh(3, 9, 5, 1));
 
         collectionTypeLabel = builder.addLabel("Collection Type:", cc.xy(1, 11));
         builder.add(collectionTypeCombo, cc.xywh(3, 11, 1, 1));
@@ -126,17 +136,25 @@ public class ObjRelationshipInfoView extends JDialog{
         mapKeysLabel = builder.addLabel("Map Key:", cc.xy(1, 13));
         builder.add(mapKeysCombo, cc.xywh(3, 13, 1, 1));
 
-        builder.addSeparator("Mapping to DbRelationships", cc.xywh(1, 15, 5, 1));
+        builder.addLabel("Delete rule:", cc.xy(1, 15));
+        builder.add(deleteRule, cc.xywh(3, 15, 1, 1));
+
+        builder.addLabel("Used for locking:", cc.xy(1, 17));
+        builder.add(usedForLocking, cc.xywh(3, 17, 1, 1));
+
+        builder.addLabel("Comment:", cc.xy(1, 19));
+        builder.add(comment, cc.xywh(3, 19, 1, 1));
+
+        builder.addSeparator("Mapping to DbRelationships", cc.xywh(1, 21, 5, 1));
 
         JPanel buttonsPane = new JPanel(new FlowLayout(FlowLayout.LEADING));
-        buttonsPane.add(selectPathButton);
         buttonsPane.add(newRelButton);
 
-        builder.add(buttonsPane, cc.xywh(1, 17, 5, 1));
+        builder.add(buttonsPane, cc.xywh(1, 23, 5, 1));
         builder.add(new JScrollPane(
                 pathBrowser,
                 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
-                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), cc.xywh(1, 19, 5, 3));
+                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), cc.xywh(1, 25, 5, 3));
 
         add(builder.getPanel(), BorderLayout.CENTER);
         JButton[] buttons = {cancelButton, saveButton};
@@ -157,24 +175,18 @@ public class ObjRelationshipInfoView extends JDialog{
     {
         return newRelButton;
     }
-    
-    public JButton getSelectPathButton()
-    {
-        return selectPathButton;
-    }
-    
+
     public JTextField getRelationshipName()
     {
         return relationshipName;
     }
     
-    public JLabel getCurrentPathLabel()
+    public JLabel getSemanticsLabel()
     {
-        return currentPathLabel;
+        return semanticsLabel;
     }
     
-    public JLabel getSourceEntityLabel()
-    {
+    public JLabel getSourceEntityLabel() {
         return sourceEntityLabel;
     }
     
@@ -183,11 +195,35 @@ public class ObjRelationshipInfoView extends JDialog{
         return targetCombo;
     }
     
-    public JComboBox getCollectionTypeCombo() {
+    public JComboBox<String> getCollectionTypeCombo() {
         return collectionTypeCombo;
     }
     
-    public JComboBox getMapKeysCombo() {
+    public JComboBox<String> getMapKeysCombo() {
         return mapKeysCombo;
     }
+
+    public JComboBox getDeleteRule() {
+        return deleteRule;
+    }
+
+    public JCheckBox getUsedForLocking() {
+        return usedForLocking;
+    }
+
+    public JTextField getComment() {
+        return comment;
+    }
+
+    public Component getMapKeysLabel() {
+        return mapKeysLabel;
+    }
+
+    public Component getCollectionTypeLabel() {
+        return collectionTypeLabel;
+    }
+
+    public MultiColumnBrowser getPathBrowser() {
+        return pathBrowser;
+    }
 }
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjEntityRelationshipPanel.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjEntityRelationshipPanel.java
index 466ccb7..cd325b2 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjEntityRelationshipPanel.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjEntityRelationshipPanel.java
@@ -18,6 +18,34 @@
  ****************************************************************/
 package org.apache.cayenne.modeler.editor;
 
+import javax.swing.BorderFactory;
+import javax.swing.DefaultCellEditor;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.UIManager;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.List;
+
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DeleteRule;
 import org.apache.cayenne.map.ObjEntity;
@@ -50,34 +78,6 @@ import org.apache.cayenne.modeler.util.UIUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.swing.BorderFactory;
-import javax.swing.DefaultCellEditor;
-import javax.swing.Icon;
-import javax.swing.ImageIcon;
-import javax.swing.JComboBox;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-import javax.swing.JMenuItem;
-import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
-import javax.swing.JTable;
-import javax.swing.ListSelectionModel;
-import javax.swing.UIManager;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.table.DefaultTableCellRenderer;
-import javax.swing.table.TableCellRenderer;
-import javax.swing.table.TableColumn;
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.List;
-
 /**
  * Displays ObjRelationships for the edited ObjEntity.
  */
@@ -183,7 +183,9 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
             }
 
             ObjRelationshipTableModel model = (ObjRelationshipTableModel) table.getModel();
-            new ObjRelationshipInfo(mediator, model.getRelationship(row)).startupAction();
+            new ObjRelationshipInfo(mediator)
+                    .modifyRelationship(model.getRelationship(row))
+                    .startupAction();
 
             // This is required for a table to be updated properly
             table.cancelEditing();
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RelationshipUndoableEdit.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RelationshipUndoableEdit.java
index f230c05..29dcb9d 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RelationshipUndoableEdit.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/undo/RelationshipUndoableEdit.java
@@ -23,6 +23,9 @@ import javax.swing.undo.CannotUndoException;
 
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.Relationship;
 import org.apache.cayenne.map.event.MapEvent;
 import org.apache.cayenne.map.event.RelationshipEvent;
 import org.apache.cayenne.modeler.Application;
@@ -30,28 +33,40 @@ import org.apache.cayenne.modeler.ProjectController;
 
 public class RelationshipUndoableEdit extends CayenneUndoableEdit {
 
-    private DbRelationship relationship;
-    private DbRelationship prevRelationship;
+	private static final long serialVersionUID = -1864303176024098961L;
+
+	private Relationship relationship;
+    private Relationship prevRelationship;
     private ProjectController projectController;
+    private boolean useDb;
 
-	public RelationshipUndoableEdit(DbRelationship relationship) {
+	public RelationshipUndoableEdit(Relationship relationship) {
 		this.projectController = Application.getInstance().getFrameController().getProjectController();
 		this.relationship = relationship;
+		this.useDb = relationship instanceof DbRelationship;
 		this.prevRelationship = copyRelationship(relationship);
 	}
 
     @Override
 	public void redo() throws CannotRedoException {
-		fireDbRelationshipEvent(relationship, prevRelationship);
+		fireRelationshipEvent(relationship, prevRelationship);
 	}
 
 	@Override
 	public void undo() throws CannotUndoException {
-		fireDbRelationshipEvent(prevRelationship, relationship);
+		fireRelationshipEvent(prevRelationship, relationship);
+	}
+
+	private void fireRelationshipEvent(Relationship relToFire, Relationship currRel) {
+		if(useDb) {
+			fireDbRelationshipEvent(relToFire, currRel);
+		} else {
+			fireObjRelationshipEvent(relToFire, currRel);
+		}
 	}
 
-	private void fireDbRelationshipEvent(DbRelationship relToFire, DbRelationship currRel) {
-		DbEntity dbEntity = currRel.getSourceEntity();
+	private void fireDbRelationshipEvent(Relationship relToFire, Relationship currRel) {
+		DbEntity dbEntity = ((DbRelationship)currRel).getSourceEntity();
 		dbEntity.removeRelationship(currRel.getName());
 		dbEntity.addRelationship(relToFire);
 		projectController
@@ -59,6 +74,15 @@ public class RelationshipUndoableEdit extends CayenneUndoableEdit {
 						new RelationshipEvent(this, relToFire, relToFire.getSourceEntity(), MapEvent.ADD));
 	}
 
+	private void fireObjRelationshipEvent(Relationship relToFire, Relationship currRel) {
+		ObjEntity objEntity = ((ObjRelationship) currRel).getSourceEntity();
+		objEntity.removeRelationship(currRel.getName());
+		objEntity.addRelationship(relToFire);
+		projectController
+				.fireObjRelationshipEvent(
+						new RelationshipEvent(this, relToFire, relToFire.getSourceEntity(), MapEvent.ADD));
+	}
+
 	@Override
 	public String getRedoPresentationName() {
 		return "Redo Edit relationship";
@@ -69,14 +93,31 @@ public class RelationshipUndoableEdit extends CayenneUndoableEdit {
 		return "Undo Edit relationship";
 	}
 
-	private DbRelationship copyRelationship(DbRelationship dbRelationship) {
+	private Relationship copyRelationship(Relationship relationship) {
+		return useDb ? getDbRelationship(relationship) : getObjRelationship(relationship);
+	}
+
+	private DbRelationship getDbRelationship(Relationship dbRelationship) {
 		DbRelationship rel = new DbRelationship();
 		rel.setName(dbRelationship.getName());
-		rel.setToDependentPK(dbRelationship.isToDependentPK());
+		rel.setToDependentPK(((DbRelationship)dbRelationship).isToDependentPK());
 		rel.setToMany(dbRelationship.isToMany());
 		rel.setTargetEntityName(dbRelationship.getTargetEntityName());
 		rel.setSourceEntity(dbRelationship.getSourceEntity());
 		rel.setJoins(rel.getJoins());
 		return rel;
 	}
+
+	private ObjRelationship getObjRelationship(Relationship objRelationship) {
+		ObjRelationship rel = new ObjRelationship();
+		rel.setName(objRelationship.getName());
+		rel.setTargetEntityName(objRelationship.getTargetEntityName());
+		rel.setSourceEntity(objRelationship.getSourceEntity());
+		rel.setDeleteRule(((ObjRelationship)objRelationship).getDeleteRule());
+		rel.setUsedForLocking(((ObjRelationship)objRelationship).isUsedForLocking());
+		rel.setDbRelationshipPath(((ObjRelationship)objRelationship).getDbRelationshipPath());
+		rel.setCollectionType(((ObjRelationship)objRelationship).getCollectionType());
+		rel.setMapKey(((ObjRelationship)objRelationship).getMapKey());
+		return rel;
+	}
 }