You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by sk...@apache.org on 2015/12/07 16:31:42 UTC

[3/8] cayenne git commit: add auto suggestion DbRelationshipPath . add extra columns relationship table. reason : reduse DatabaseMapping dialog

add auto suggestion DbRelationshipPath . add extra columns relationship table. reason : reduse DatabaseMapping dialog


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/8367f77a
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/8367f77a
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/8367f77a

Branch: refs/heads/master
Commit: 8367f77a52c910a2dc56a49668b068edf402e713
Parents: 681a1fa
Author: AlexandrShestak <sh...@mail.ru>
Authored: Tue Nov 24 12:34:32 2015 +0300
Committer: Savva Kolbachev <s....@gmail.com>
Committed: Mon Dec 7 13:21:41 2015 +0300

----------------------------------------------------------------------
 .../editor/ObjEntityRelationshipPanel.java      | 535 ++++++++++++++++++-
 .../editor/ObjRelationshipTableModel.java       |  69 ++-
 2 files changed, 586 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/8367f77a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjEntityRelationshipPanel.java
----------------------------------------------------------------------
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 3b557e2..dffd96d 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,10 +18,15 @@
  ****************************************************************/
 package org.apache.cayenne.modeler.editor;
 
+import org.apache.cayenne.map.Attribute;
 import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.DeleteRule;
+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.EntityEvent;
 import org.apache.cayenne.map.event.ObjEntityListener;
 import org.apache.cayenne.map.event.ObjRelationshipListener;
@@ -40,13 +45,17 @@ import org.apache.cayenne.modeler.event.TablePopupHandler;
 import org.apache.cayenne.modeler.pref.TableColumnPreferences;
 import org.apache.cayenne.modeler.util.CayenneTable;
 import org.apache.cayenne.modeler.util.CellRenderers;
+import org.apache.cayenne.modeler.util.EntityTreeFilter;
+import org.apache.cayenne.modeler.util.EntityTreeModel;
 import org.apache.cayenne.modeler.util.ModelerUtil;
 import org.apache.cayenne.modeler.util.PanelFactory;
 import org.apache.cayenne.modeler.util.UIUtil;
 import org.apache.cayenne.modeler.util.combo.AutoCompletion;
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import javax.swing.AbstractCellEditor;
 import javax.swing.DefaultCellEditor;
 import javax.swing.DefaultComboBoxModel;
 import javax.swing.Icon;
@@ -62,14 +71,22 @@ import javax.swing.event.ListSelectionListener;
 import javax.swing.event.TableModelEvent;
 import javax.swing.event.TableModelListener;
 import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
 import javax.swing.table.TableColumn;
+import javax.swing.text.JTextComponent;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
+import java.awt.Font;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.regex.Pattern;
 
 /**
  * Displays ObjRelationships for the edited ObjEntity.
@@ -86,6 +103,11 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
             DeleteRule.deleteRuleName(DeleteRule.DENY),
     };
 
+ /*   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_COLLECTION_TYPE = "java.util.List";*/
+
     protected ProjectController mediator;
     protected CayenneTable table;
     private TableColumnPreferences tablePreferences;
@@ -94,7 +116,7 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
     private boolean enabledResolve;//for JBottom "resolve" in ObjEntityAttrRelationshipTab
 
     /**
-     * By now popup menu item is made similiar to toolbar button. (i.e. all functionality
+     * By now popup menu item is made similar to toolbar button. (i.e. all functionality
      * is here) This should be probably refactored as Action.
      */
     protected JMenuItem resolveMenu;
@@ -112,7 +134,16 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
 
         ActionManager actionManager = Application.getInstance().getActionManager();
 
-        table = new CayenneTable();
+        table = new CayenneTable(){
+            @Override
+            public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
+                Component component = super.prepareRenderer(renderer, row, column);
+                int rendererWidth = component.getPreferredSize().width;
+                TableColumn tableColumn = getColumnModel().getColumn(column);
+                tableColumn.setPreferredWidth(Math.max(rendererWidth + getIntercellSpacing().width, tableColumn.getPreferredWidth()));
+                return component;
+            }
+        };
         table.setDefaultRenderer(String.class, new StringRenderer());
         table.setDefaultRenderer(ObjEntity.class, new EntityRenderer());
         tablePreferences = new TableColumnPreferences(
@@ -287,7 +318,8 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
 
         JComboBox combo = (JComboBox) editor.getComponent();
         combo.setRenderer(CellRenderers.entityListRendererWithIcons(entity.getDataMap()));
-        combo.setModel(new DefaultComboBoxModel(createObjEntityComboModel()));
+        //combo.setModel(new DefaultComboBoxModel(createObjEntityComboModel()));
+        //combo.setEnabled(false);
 
         ObjRelationshipTableModel model = (ObjRelationshipTableModel) table.getModel();
         model.fireTableDataChanged();
@@ -319,7 +351,7 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
         table.setRowMargin(3);
 
         TableColumn col = table.getColumnModel().getColumn(
-                ObjRelationshipTableModel.REL_TARGET);
+                ObjRelationshipTableModel.REL_TARGET_PATH);
         JComboBox targetCombo = Application.getWidgetFactory().createComboBox(
                 createObjEntityComboModel(),
                 false);
@@ -328,9 +360,9 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
         targetCombo.setRenderer(CellRenderers.entityListRendererWithIcons(entity
                 .getDataMap()));
         targetCombo.setSelectedIndex(-1);
-        col.setCellEditor(Application.getWidgetFactory().createCellEditor(targetCombo));
+        col.setCellEditor(new JTableTargetComboBoxEditor());
 
-        col = table.getColumnModel().getColumn(ObjRelationshipTableModel.REL_DELETERULE);
+        col = table.getColumnModel().getColumn(ObjRelationshipTableModel.REL_DELETE_RULE);
         JComboBox deleteRulesCombo = Application.getWidgetFactory().createComboBox(
                 deleteRules,
                 false);
@@ -339,6 +371,15 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
         col.setCellEditor(Application.getWidgetFactory().createCellEditor(
                 deleteRulesCombo));
 
+        col = table.getColumnModel().getColumn(ObjRelationshipTableModel.REL_COLLECTION_TYPE);
+
+        col.setCellEditor(new JTableCollectionTypeComboBoxEditor());
+        col.setCellRenderer(new JTableCollectionTypeComboBoxRenderer());
+
+        col = table.getColumnModel().getColumn(ObjRelationshipTableModel.REL_MAP_KEY);
+        col.setCellEditor(new JTableMapKeyComboBoxEditor());
+        col.setCellRenderer(new JTableMapKeyComboBoxRenderer());
+
         tablePreferences.bind(
                 table,
                 null,
@@ -469,4 +510,486 @@ public class ObjEntityRelationshipPanel extends JPanel implements ObjEntityDispl
     public ActionListener getResolver() {
         return resolver;
     }
+
+    private final static class JTableCollectionTypeComboBoxEditor extends AbstractCellEditor implements TableCellEditor{
+
+        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_COLLECTION_TYPE = "java.util.List";
+
+        private ObjRelationshipTableModel model;
+        private int row;
+        private int column;
+
+        public JTableCollectionTypeComboBoxEditor() {
+        }
+
+        @Override
+        public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int row, final int column) {
+            this.model = (ObjRelationshipTableModel) table.getModel();
+            this.row = row;
+            this.column = column;
+
+            final JComboBox collectionTypeCombo = Application.getWidgetFactory().createComboBox(
+                    new Object[]{
+                            COLLECTION_TYPE_MAP,
+                            COLLECTION_TYPE_SET,
+                            COLLECTION_TYPE_COLLECTION,
+                            DEFAULT_COLLECTION_TYPE
+                    },
+                    false);
+            if(model.getRelationship(row).isToMany()){
+                collectionTypeCombo.setEnabled(true);
+                collectionTypeCombo.setSelectedItem( model.getRelationship(row).getCollectionType());
+            }else{
+                JLabel labelIfToOneRelationship = new JLabel();
+                labelIfToOneRelationship.setEnabled(false);
+                //collectionTypeCombo.setEnabled(false);
+                //collectionTypeCombo.setSelectedItem( model.getRelationship(row).getCollectionType());
+                return labelIfToOneRelationship;
+            }
+            collectionTypeCombo.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    Object selected = collectionTypeCombo.getSelectedItem();
+                    model.setUpdatedValueAt(selected,row,column);
+                    table.repaint();
+                }
+            });
+            return collectionTypeCombo;
+        }
+
+        @Override
+        public Object getCellEditorValue() {
+            return model.getValueAt(row,column);
+        }
+    }
+
+    private final static class JTableCollectionTypeComboBoxRenderer implements TableCellRenderer {
+
+        private ObjRelationshipTableModel model;
+
+        public JTableCollectionTypeComboBoxRenderer() {
+        }
+
+        @Override
+        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+            this.model = (ObjRelationshipTableModel) table.getModel();
+            JLabel labelIfToOneRelationship = new JLabel();
+            labelIfToOneRelationship.setEnabled(false);
+            JLabel labelIfToManyRelationship = new JLabel((String) value);
+            labelIfToManyRelationship.setEnabled(true);
+            labelIfToManyRelationship.setFont(new Font("Verdana", Font.PLAIN , 12));
+            if (value == null)
+                return labelIfToOneRelationship;
+
+            if (model.getRelationship(row).isToMany()) {
+                return labelIfToManyRelationship;
+            }else{
+                return labelIfToOneRelationship;
+            }
+
+        }
+    }
+
+    private final static class JTableMapKeyComboBoxEditor extends AbstractCellEditor implements TableCellEditor {
+
+        static final String DEFAULT_MAP_KEY = "ID (default)";
+        static final String COLLECTION_TYPE_MAP = "java.util.Map";
+
+        private List<String> mapKeys = new ArrayList<>() ;
+        private ObjRelationshipTableModel model;
+        private int row;
+        private int column;
+
+        private JTableMapKeyComboBoxEditor() {
+        }
+
+        private void initMapKeys() {
+            mapKeys.clear();
+            mapKeys.add(DEFAULT_MAP_KEY);
+            /**
+             * Object target can be null when selected target DbEntity has no
+             * ObjEntities
+             */
+            ObjEntity objectTarget = model.getRelationship(row).getTargetEntity();
+            if (objectTarget == null) {
+                return ;
+            }
+            for (ObjAttribute attribute : objectTarget.getAttributes()) {
+                mapKeys.add(attribute.getName());
+            }
+        }
+
+        @Override
+        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, final int row, final int column) {
+            this.model = (ObjRelationshipTableModel) table.getModel();
+            this.row = row;
+            this.column = column;
+            initMapKeys();
+            final JComboBox mapKeysComboBox =  Application.getWidgetFactory().createComboBox(
+                  mapKeys,
+                    false);
+            if ((model.getRelationship(row).getCollectionType() == null)
+                    ||(!model.getRelationship(row).getCollectionType().equals(COLLECTION_TYPE_MAP))){
+                JComboBox jComboBox = new JComboBox();
+                jComboBox.setFocusable(false);
+                jComboBox.setEnabled(false);
+                return jComboBox;
+            }else{
+                mapKeysComboBox.setFocusable(true);
+                mapKeysComboBox.setEnabled(true);
+            }
+            mapKeysComboBox.addActionListener(new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e) {
+                    Object selected = mapKeysComboBox.getSelectedItem();
+                    model.setUpdatedValueAt(selected,row,column);
+                }
+            });
+            mapKeysComboBox.setSelectedItem(model.getRelationship(row).getMapKey());
+            return mapKeysComboBox;
+        }
+
+        @Override
+        public Object getCellEditorValue() {
+            return model.getValueAt(row,column);
+        }
+    }
+
+    private final static class JTableMapKeyComboBoxRenderer implements TableCellRenderer{
+
+        static final String DEFAULT_MAP_KEY = "ID (default)";
+        static final String COLLECTION_TYPE_MAP = "java.util.Map";
+
+        private ObjRelationshipTableModel model;
+
+        public JTableMapKeyComboBoxRenderer() {
+        }
+
+        @Override
+        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+            this.model = (ObjRelationshipTableModel) table.getModel();
+            if ( (model.getRelationship(row).getCollectionType() == null)
+                ||(!model.getRelationship(row).getCollectionType().equals(COLLECTION_TYPE_MAP))){
+                JComboBox jComboBox = new JComboBox();
+                jComboBox.setFocusable(false);
+                jComboBox.setEnabled(false);
+                return jComboBox;
+            }
+            if (model.getRelationship(row).getMapKey() == null){
+                model.getRelationship(row).setMapKey(DEFAULT_MAP_KEY);
+            }
+            JLabel jLabel  = new JLabel(model.getRelationship(row).getMapKey());
+            jLabel.setFont(new Font("Verdana", Font.PLAIN , 12));
+            return jLabel;
+        }
+    }
+
+    private static final class JTableTargetComboBoxEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
+
+        private ObjRelationshipTableModel model;
+        private int row;
+        private int column;
+        private JComboBox dbRelationshipPathCombo;
+        private EntityTreeModel treeModel;
+        private int previousEmbededLevel = 0;
+        private static int enterPressedCount = 0;
+
+        public JTableTargetComboBoxEditor() {
+        }
+
+        @Override
+        public Object getCellEditorValue() {
+            return model.getValueAt(row,column);
+        }
+
+        @Override
+        public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int row, int column) {
+            this.model = (ObjRelationshipTableModel) table.getModel();
+            this.row = row;
+            this.column = column;
+            treeModel = createTreeModelForComboBoxBrowser(row);
+            if (treeModel == null)
+                return new JLabel("You need select table to this ObjectEntity");
+            initializeCombo(model , row);
+
+            String dbRelationshipPath = ((JTextComponent) (dbRelationshipPathCombo).
+                    getEditor().getEditorComponent()).getText();
+            previousEmbededLevel =  dbRelationshipPath.split(Pattern.quote(".")).length;
+
+            dbRelationshipPathCombo.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
+                private void enterPressed(){
+                    String dbRelationshipPath = ((JTextComponent) (dbRelationshipPathCombo).
+                            getEditor().getEditorComponent()).getText();
+                    Object currentNode = getCurrentNode(dbRelationshipPath);
+                    String[] pathStrings = dbRelationshipPath.split(Pattern.quote("."));
+                    String lastStringInPath = pathStrings[pathStrings.length - 1];
+
+                    if (lastStringInPath.equals(ModelerUtil.getObjectName(currentNode))) {
+                        if (enterPressedCount == 1) {
+                            //it is second time enter pressed.. so we will save input data
+                            enterPressedCount = 0;
+                            if (currentNode instanceof DbRelationship) {
+
+                                if (table.getCellEditor() != null) {
+
+                                    table.getCellEditor().stopCellEditing();
+                                    model.getRelationship(row).setDbRelationshipPath(dbRelationshipPath);
+
+                                    //we need object target to save it in model
+                                    DbEntity lastEntity = ((DbRelationship) currentNode).getTargetEntity();
+                                    Collection<ObjEntity> objEntities = ((DbRelationship) currentNode).getTargetEntity().
+                                            getDataMap().getMappedEntities(lastEntity);
+                                    ObjEntity objectTarget = objEntities.size() == 0 ? null : objEntities.iterator().next();
+                                    model.getRelationship(row).setTargetEntityName(objectTarget);
+                                }
+                            }
+                            table.repaint();
+                        } else {
+                            enterPressedCount = 1;
+                        }
+                    }
+                }
+
+                @Override
+                public void keyReleased(KeyEvent event) {
+                    if (event.getKeyCode() == KeyEvent.VK_ENTER) {
+                        enterPressed();
+                        return;
+                    }
+                    parseDbRelationshipString(event.getKeyChar());
+                }
+            });
+            return dbRelationshipPathCombo;
+        }
+
+        private void initializeCombo(ObjRelationshipTableModel model , int row){
+            String dbRelationshipPath = model.getRelationship(row).getDbRelationshipPath();
+            Object currentNode;
+            if (dbRelationshipPath == null){
+                //case if it is new attribute or for some reason dbRelationshipPath is null
+                currentNode = getCurrentNode(dbRelationshipPath);
+                dbRelationshipPath = "";
+
+            }else{
+                //case if  dbRelationshipPath isn't null and we must change it to find auto completion list
+                String[] pathStrings = dbRelationshipPath.split(Pattern.quote("."));
+                String lastStringInPath = pathStrings[pathStrings.length - 1];
+                dbRelationshipPath = dbRelationshipPath.replaceAll(lastStringInPath + "$", "");
+                currentNode = getCurrentNode(dbRelationshipPath);
+            }
+            List<String> nodeChildren = getChildren(currentNode , dbRelationshipPath);
+            dbRelationshipPathCombo = Application.getWidgetFactory().createComboBox(
+                    nodeChildren,
+                    false);
+            AutoCompletion.enable(dbRelationshipPathCombo, false, true);
+            dbRelationshipPathCombo.setEditable(true);
+            ((JTextComponent) (dbRelationshipPathCombo).
+                    getEditor().getEditorComponent()).setText(model.getRelationship(row).getDbRelationshipPath());
+            dbRelationshipPathCombo.setSelectedItem(model.getRelationship(row).getDbRelationshipPath());
+            dbRelationshipPathCombo.addActionListener(this);
+            return;
+        }
+
+        /*
+         * chech if potential child is child for father
+         * @param father
+         * @param potentialChild
+         * @return
+         */
+        /*private boolean isChild(Object father , Object potentialChild){
+            List<Object> fatherChildren = new ArrayList<>();
+            for(int j = 0 ; j <  treeModel.getChildCount(father) ; j++){
+                Object child = treeModel.getChild(father, j);
+                fatherChildren.add(child);
+            }
+            return fatherChildren.contains(potentialChild);
+        }*/
+
+        private void parseDbRelationshipString(char lastEnteredCharacter){
+            String dbRelationshipPath = ((JTextComponent) (dbRelationshipPathCombo).
+                    getEditor().getEditorComponent()).getText();
+
+            enterPressedCount = 0;
+
+            if (dbRelationshipPath.equals("")){
+                List<String> currentNodeChildren = new ArrayList<>();
+                currentNodeChildren.add("");
+                currentNodeChildren.addAll(getChildren(getCurrentNode(dbRelationshipPath),""));
+                dbRelationshipPathCombo.setModel(new DefaultComboBoxModel(currentNodeChildren.toArray()));
+                dbRelationshipPathCombo.showPopup();
+                dbRelationshipPathCombo.setPopupVisible(true);
+                return;
+            }
+
+            if (lastEnteredCharacter == '.') {
+                processDotEntered();
+                previousEmbededLevel  =  StringUtils.countMatches(dbRelationshipPath,".");
+                return;
+            }
+
+            int currentEmbededLevel =  StringUtils.countMatches(dbRelationshipPath,".");
+            if (previousEmbededLevel != currentEmbededLevel){
+                previousEmbededLevel = currentEmbededLevel;
+                List<String> currentNodeChildren = new ArrayList<>();
+                String[] pathStrings = dbRelationshipPath.split(Pattern.quote("."));
+                String lastStringInPath = pathStrings[pathStrings.length - 1];
+                String saveDbRelationshipPath = dbRelationshipPath;
+                dbRelationshipPath = dbRelationshipPath.replaceAll(lastStringInPath + "$", "");
+                currentNodeChildren.add("");
+                currentNodeChildren.addAll(getChildren(getCurrentNode(dbRelationshipPath), dbRelationshipPath));
+                dbRelationshipPathCombo.setModel(new DefaultComboBoxModel(currentNodeChildren.toArray()));
+                ((JTextComponent) (dbRelationshipPathCombo).
+                        getEditor().getEditorComponent()).setText(saveDbRelationshipPath);
+
+                dbRelationshipPathCombo.showPopup();
+                dbRelationshipPathCombo.setPopupVisible(true);
+                return;
+            }
+        }
+
+        private void processDotEntered(){
+            String dbAttributePath = ((JTextComponent) (dbRelationshipPathCombo).
+                    getEditor().getEditorComponent()).getText();
+            if (dbAttributePath.equals(".")){
+                List<String> currentNodeChildren = new ArrayList<>();
+                currentNodeChildren.add("");
+                currentNodeChildren.addAll(getChildren(getCurrentNode(""),""));
+                dbRelationshipPathCombo.setModel(new DefaultComboBoxModel(currentNodeChildren.toArray()));
+                dbRelationshipPathCombo.showPopup();
+                dbRelationshipPathCombo.setPopupVisible(true);
+                return;
+            }else {
+                char secondFromEndCharacter = dbAttributePath.charAt(dbAttributePath.length()-2);
+                if(secondFromEndCharacter == '.') {
+                    // two dots entered one by one , we replace it by one dot
+                    ((JTextComponent) (dbRelationshipPathCombo).
+                            getEditor().getEditorComponent()).setText(dbAttributePath.substring(0,dbAttributePath.length()-1));
+                    return;
+                }else{
+                    String[] pathStrings = dbAttributePath.split(Pattern.quote("."));
+                    String lastStringInPath = pathStrings[pathStrings.length - 1];
+
+                    //we will check if lastStringInPath is correct name of DbAttribute or DbRelationship
+                    //for appropriate previous node in path. if it is not we won't add entered dot to dbAttributePath
+                    String dbAttributePathForPreviousNode;
+                    if (pathStrings.length == 1){
+                        dbAttributePathForPreviousNode = null;
+                    }else {
+                        dbAttributePathForPreviousNode = dbAttributePath.replace("."+lastStringInPath,"");
+                    }
+                    List<String> potentialVariantsToChoose = getChildren(getCurrentNode(dbAttributePathForPreviousNode),"");
+                    if (potentialVariantsToChoose.contains(lastStringInPath)){
+                        List<String> currentNodeChildren = new ArrayList<>();
+                        currentNodeChildren.add(dbAttributePath + "");
+                        currentNodeChildren.addAll(getChildren(getCurrentNode(dbAttributePath), dbAttributePath));
+                        dbRelationshipPathCombo.setModel(new DefaultComboBoxModel(currentNodeChildren.toArray()));
+                        dbRelationshipPathCombo.showPopup();
+                        dbRelationshipPathCombo.setPopupVisible(true);
+                    }else{
+                        ((JTextComponent) (dbRelationshipPathCombo).
+                                getEditor().getEditorComponent()).setText(dbAttributePath.substring(0,dbAttributePath.length()-1));
+                    }
+                }
+            }
+            previousEmbededLevel =  StringUtils.countMatches(dbAttributePath,".");
+            return;
+        }
+
+        /**
+         * find current node by dbRelationshipPath
+         * @param dbRelationshipPath
+         * @return last node in dbRelationshipPath which matches DbRelationship
+         */
+        private final Object getCurrentNode(String dbRelationshipPath) {
+            try {
+                //case for new relationship
+                if(dbRelationshipPath == null){
+                    return treeModel.getRoot();
+                }
+                String[] pathStrings = dbRelationshipPath.split(Pattern.quote("."));
+                Object root = treeModel.getRoot();
+                for (int  i = 0 ; i < pathStrings.length ; i ++) {
+                    String rootChildText = pathStrings[i];
+                    for (int j = 0; j < treeModel.getChildCount(root); j++) {
+                        Object child = treeModel.getChild(root, j);
+                        if (child instanceof DbRelationship) {
+                            String relationshipName = ModelerUtil.getObjectName(child);
+                            if (relationshipName.equals(rootChildText)) {
+                                root = child;
+                                break;
+                            }
+                        }
+                    }
+                }
+                return root;
+            }catch (Exception e){
+                return treeModel.getRoot();
+            }
+        }
+
+        /**
+         * @param node for which we will find children
+         * @param dbRelationshipPath string which will be added to each child to make right autocomplete
+         * @return list with children , which will be used to autocomplete
+         */
+        private final List<String> getChildren(Object node , String dbRelationshipPath){
+            List<String> currentNodeChildren = new ArrayList<>();
+            for(int j = 0 ; j <  treeModel.getChildCount(node) ; j++){
+                Object child = treeModel.getChild(node, j);
+                String relationshipName = ModelerUtil.getObjectName(child);
+                currentNodeChildren.add(dbRelationshipPath + relationshipName);
+            }
+            return currentNodeChildren;
+        }
+
+        /**
+         * @param relationshipIndexInTable index of attribute for which now we will create cell editor
+         * @return treeModel for nessesary for us attribute
+         */
+        private EntityTreeModel createTreeModelForComboBoxBrowser(int relationshipIndexInTable){
+            if (model.getRelationship(relationshipIndexInTable).
+                    getSourceEntity().getDbEntity() == null)
+                return null;
+            EntityTreeModel treeModel = new EntityTreeModel(model.getRelationship(relationshipIndexInTable).
+                    getSourceEntity().getDbEntity());
+            treeModel.setFilter(new EntityTreeFilter() {
+
+                public boolean attributeMatch(Object node, Attribute attr) {
+                    // attrs not allowed here
+                    return false;
+                }
+
+                public boolean relationshipMatch(Object node, Relationship rel) {
+                    if (!(node instanceof Relationship)) {
+                        return true;
+                    }
+
+                    /**
+                     * We do not allow A->B->A chains, where relationships are
+                     * to-one
+                     */
+                    DbRelationship prev = (DbRelationship) node;
+                    return !(!rel.isToMany() && prev.getReverseRelationship() == rel);
+                }
+
+            });
+            return treeModel;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            model.getRelationship(row).setMapKey(null);
+
+            //for some reason dbRelationshipPathCombo don't load selected item text, so we made it by hand
+            if (dbRelationshipPathCombo.getSelectedIndex() != (-1)){
+                ((JTextComponent) (dbRelationshipPathCombo).
+                        getEditor().getEditorComponent()).setText(dbRelationshipPathCombo.getSelectedItem().toString());
+            }
+
+
+        }
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8367f77a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjRelationshipTableModel.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjRelationshipTableModel.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjRelationshipTableModel.java
index 5dca446..91592f6 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjRelationshipTableModel.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/ObjRelationshipTableModel.java
@@ -39,9 +39,14 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
     // Columns
     static final int REL_NAME = 0;
     static final int REL_TARGET = 1;
-    static final int REL_SEMANTICS = 2;
-    static final int REL_DELETERULE = 3;
-    static final int REL_LOCKING = 4;
+    static final int REL_TARGET_PATH = 2;
+    static final int REL_COLLECTION_TYPE = 3;
+    static final int REL_MAP_KEY = 4;
+    static final int REL_SEMANTICS = 5;
+    static final int REL_DELETE_RULE = 6;
+    static final int REL_LOCKING = 7;
+
+    static final int COLUMN_COUNT = 8;
 
     protected ObjEntity entity;
 
@@ -72,7 +77,7 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
     }
 
     public int getColumnCount() {
-        return 5;
+        return COLUMN_COUNT;
     }
 
     @Override
@@ -86,8 +91,14 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
                 return "Used for Locking";
             case REL_SEMANTICS:
                 return "Semantics";
-            case REL_DELETERULE:
+            case REL_DELETE_RULE:
                 return "Delete Rule";
+            case REL_COLLECTION_TYPE:
+                return "Collection Type";
+            case REL_MAP_KEY:
+                return "Map key";
+            case REL_TARGET_PATH:
+                return "DbRelationshipPath";
 
             default:
                 return null;
@@ -126,8 +137,16 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
         else if (column == REL_SEMANTICS) {
             return getSemantics(relationship);
         }
-        else if (column == REL_DELETERULE) {
+        else if (column == REL_DELETE_RULE) {
             return DeleteRule.deleteRuleName(relationship.getDeleteRule());
+        } else if (column == REL_COLLECTION_TYPE) {
+            if (!relationship.isToMany())
+                return null;
+            return relationship.getCollectionType();
+        } else if (column == REL_MAP_KEY){
+            return relationship.getMapKey();
+        } else if (column == REL_TARGET_PATH){
+            return relationship.getDbRelationshipPath();
         }
         else {
             return null;
@@ -191,7 +210,7 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
 
             fireTableRowsUpdated(row, row);
         }
-        else if (column == REL_DELETERULE) {
+        else if (column == REL_DELETE_RULE) {
             relationship.setDeleteRule(DeleteRule.deleteRuleForName((String) value));
             fireTableCellUpdated(row, column);
         }
@@ -200,6 +219,17 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
                     && ((Boolean) value).booleanValue());
             fireTableCellUpdated(row, column);
         }
+        else if (column == REL_COLLECTION_TYPE){
+            relationship.setCollectionType((String) value);
+            fireTableCellUpdated(row, column);
+        }else if (column == REL_MAP_KEY){
+            relationship.setMapKey((String) value);
+            fireTableCellUpdated(row, column);
+            }
+        else if (column == REL_TARGET_PATH){
+            relationship.setDbRelationshipPath((String) value);
+            fireTableCellUpdated(row, column);
+        }
 
         mediator.fireObjRelationshipEvent(event);
     }
@@ -223,7 +253,8 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
 
     @Override
     public boolean isCellEditable(int row, int col) {
-        return !isInherited(row) && col != REL_SEMANTICS;
+        return !isInherited(row) && col != REL_SEMANTICS
+                && col != REL_TARGET;
     }
 
     final class RelationshipComparator implements Comparator {
@@ -261,7 +292,10 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
                 sortByElementProperty("usedForLocking", isAscent);
                 break;
             case REL_SEMANTICS:
-            case REL_DELETERULE:
+            case REL_COLLECTION_TYPE:
+            case REL_MAP_KEY:
+            case REL_DELETE_RULE:
+            case REL_TARGET_PATH:
                 Collections.sort(objectList, new Comparator<ObjRelationship>() {
 
                     public int compare(ObjRelationship o1, ObjRelationship o2) {
@@ -278,14 +312,25 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
                         String valueToCompare1 = "";
                         String valueToCompare2 = "";
                         switch(sortCol){
+                            case REL_COLLECTION_TYPE:
+                                valueToCompare1 = o1.getCollectionType();
+                                valueToCompare2 = o2.getCollectionType();
+                                break;
+                            case REL_MAP_KEY:
+                                valueToCompare1 = o1.getMapKey();
+                                valueToCompare2 = o2.getMapKey();
+                                break;
                             case REL_SEMANTICS:
                                 valueToCompare1 = getSemantics(o1);
                                 valueToCompare2 = getSemantics(o2);
                                 break;
-                            case REL_DELETERULE:
+                            case REL_DELETE_RULE:
                                 valueToCompare1 = DeleteRule.deleteRuleName(o1.getDeleteRule());
                                 valueToCompare2 = DeleteRule.deleteRuleName(o2.getDeleteRule());
-                                
+                                break;
+                            case REL_TARGET_PATH:
+                                valueToCompare1 = o1.getDbRelationshipPath();
+                                valueToCompare2 = o2.getDbRelationshipPath();
                                 break;
                         }
                         return (valueToCompare1 == null) ? -1 : (valueToCompare2 == null)
@@ -298,7 +343,7 @@ public class ObjRelationshipTableModel extends CayenneTableModel {
                     Collections.reverse(objectList);
                 }
                 break;
-            
+
         }
 
     }