You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by an...@apache.org on 2009/12/02 15:37:45 UTC

svn commit: r886158 [1/2] - in /cayenne/main/trunk/framework: cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/ cayenne-modeler/ cayenne-modeler/src/main/java/org/apache/ca...

Author: andrey
Date: Wed Dec  2 14:37:42 2009
New Revision: 886158

URL: http://svn.apache.org/viewvc?rev=886158&view=rev
Log:
CAY-762 ERDiagram for Object Entities in Cayenne Modeler

Added:
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ModelerProject.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectConfiguration.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataDomainTabbedView.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/BaseGraphBuilder.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DataDomainGraphTab.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbEntityCellMetadata.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbGraphBuilder.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/EntityCellMetadata.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphBuilder.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphFile.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphLoader.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphMap.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphRegistry.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphType.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/ObjEntityCellMetadata.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/ObjGraphBuilder.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/action/
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/action/EntityDisplayAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/action/RefreshGraphAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/action/RemoveEntityAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/action/SaveAsImageAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/action/ZoomInAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/action/ZoomOutAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/resources/org/apache/cayenne/modeler/images/icon-refresh.png   (with props)
    cayenne/main/trunk/framework/cayenne-modeler/src/main/resources/org/apache/cayenne/modeler/images/icon-save-as-image.png   (with props)
    cayenne/main/trunk/framework/cayenne-modeler/src/main/resources/org/apache/cayenne/modeler/images/icon-zoom-in.png   (with props)
    cayenne/main/trunk/framework/cayenne-modeler/src/main/resources/org/apache/cayenne/modeler/images/icon-zoom-out.png   (with props)
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbRelationship.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Relationship.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockRelationship.java
    cayenne/main/trunk/framework/cayenne-modeler/pom.xml
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerController.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectLoadDelegate.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/NewProjectAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/OpenProjectAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ProjectAction.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbentity/DbEntityRelationshipTab.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/FileFilters.java
    cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/ModelerUtil.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbRelationship.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbRelationship.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbRelationship.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/DbRelationship.java Wed Dec  2 14:37:42 2009
@@ -461,6 +461,17 @@
     public void setToMany(boolean toMany) {
         this.toMany = toMany;
     }
+    
+    @Override
+    public boolean isMandatory() {
+        for (DbJoin join : getJoins()) {
+            if (join.getSource().isMandatory()) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
 
     final static class JoinTransformers {
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/ObjRelationship.java Wed Dec  2 14:37:42 2009
@@ -773,4 +773,14 @@
     public void setMapKey(String mapKey) {
         this.mapKey = mapKey;
     }
+    
+    @Override
+    public boolean isMandatory() {
+        refreshFromDeferredPath();
+        if (dbRelationships.size() == 0) {
+            return false;
+        }
+        
+        return dbRelationships.get(0).isMandatory();
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Relationship.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Relationship.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Relationship.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/map/Relationship.java Wed Dec  2 14:37:42 2009
@@ -177,4 +177,17 @@
     public void setRuntime(boolean synthetic) {
         this.runtime = synthetic;
     }
+    
+    /**
+     * Returns a "complimentary" relationship going in the opposite direction. Returns
+     * null if no such relationship is found.
+     * @since 3.1
+     */
+    public abstract Relationship getReverseRelationship();
+    
+    /**
+     * Returns if relationship is mandatory
+     * @since 3.1
+     */
+    public abstract boolean isMandatory();
 }

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockRelationship.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockRelationship.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockRelationship.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/map/MockRelationship.java Wed Dec  2 14:37:42 2009
@@ -38,4 +38,14 @@
 
     public void encodeAsXML(XMLEncoder encoder) {
     }
+
+    @Override
+    public Relationship getReverseRelationship() {
+        return null;
+    }
+
+    @Override
+    public boolean isMandatory() {
+        return false;
+    }
 }

Modified: cayenne/main/trunk/framework/cayenne-modeler/pom.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/pom.xml?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/pom.xml (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/pom.xml Wed Dec  2 14:37:42 2009
@@ -123,6 +123,12 @@
       		<artifactId>jedit-syntax</artifactId>
       		<version>2.2.2</version>
     	</dependency>
+    	
+    	<dependency>
+      		<groupId>jgraph</groupId>
+      		<artifactId>jgraph</artifactId>
+      		<version>5.13.0.0</version>
+    	</dependency>
   </dependencies>
 
 	<build>

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerController.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerController.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerController.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/CayenneModelerController.java Wed Dec  2 14:37:42 2009
@@ -44,7 +44,7 @@
 import org.apache.cayenne.modeler.pref.FSPath;
 import org.apache.cayenne.modeler.util.CayenneController;
 import org.apache.cayenne.pref.Domain;
-import org.apache.cayenne.project.Project;
+import org.apache.cayenne.project.ApplicationProject;
 import org.apache.cayenne.project.validator.Validator;
 
 /**
@@ -194,7 +194,7 @@
      * Handles project opening control. Updates main frame, then delegates control to
      * child controllers.
      */
-    public void projectOpenedAction(Project project) {
+    public void projectOpenedAction(ApplicationProject project) {
 
         projectController.setProject(project);
 

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ModelerProject.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ModelerProject.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ModelerProject.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ModelerProject.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,58 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler;
+
+import java.io.File;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.conf.Configuration;
+import org.apache.cayenne.modeler.action.ModelerProjectConfiguration;
+import org.apache.cayenne.modeler.graph.GraphFile;
+import org.apache.cayenne.project.ApplicationProject;
+import org.apache.cayenne.project.ProjectFile;
+
+/**
+ * Modeler-specific project. 
+ * Used to save domain graphs together with main project files
+ */
+public class ModelerProject extends ApplicationProject {
+    public ModelerProject(File projectFile, Configuration configuration) {
+        super(projectFile, configuration);
+    }
+
+    /**
+     * Returns appropriate ProjectFile or null if object does not require a file of its
+     * own. In case of ModelerProject, the nodes that require separate files are: the
+     * project itself, each DataMap, each driver DataNode, and each DataDomain that had
+     * graphs built for.
+     */
+    @Override
+    public ProjectFile projectFileForObject(Object obj) {
+        if (requiresDomainFile(obj)) {
+            return new GraphFile(this, 
+                ((ModelerProjectConfiguration) getConfiguration()).
+                    getGraphRegistry().getGraphMap((DataDomain) obj));
+        }
+        return super.projectFileForObject(obj);
+    }
+
+    protected boolean requiresDomainFile(Object obj) {
+        return obj instanceof DataDomain;
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java Wed Dec  2 14:37:42 2009
@@ -72,6 +72,7 @@
 import org.apache.cayenne.map.event.QueryEvent;
 import org.apache.cayenne.map.event.QueryListener;
 import org.apache.cayenne.map.event.RelationshipEvent;
+import org.apache.cayenne.modeler.action.ModelerProjectConfiguration;
 import org.apache.cayenne.modeler.action.NavigateBackwardAction;
 import org.apache.cayenne.modeler.action.NavigateForwardAction;
 import org.apache.cayenne.modeler.action.RevertAction;
@@ -120,6 +121,8 @@
 import org.apache.cayenne.project.ProjectPath;
 import org.apache.cayenne.query.Query;
 import org.apache.cayenne.util.IDUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * A controller that works with the project tree, tracking selection and dispatching
@@ -131,7 +134,8 @@
  * </p>
  */
 public class ProjectController extends CayenneController {
-
+    private static final Log logObj = LogFactory.getLog(ProjectController.class);
+    
     /*
      * A snapshot of the current state of the project controller. This was added so that
      * we could support history of recent objects.
@@ -257,7 +261,7 @@
     protected EventListenerList listenerList;
     protected boolean dirty;
 
-    protected Project project;
+    protected ApplicationProject project;
     protected Domain projectPreferences;
 
     protected ControllerState currentState;
@@ -268,7 +272,7 @@
      * Project files watcher. When project file is changed, user will be asked to confirm
      * loading the changes
      */
-    protected ProjectWatchdog watchdog;
+    ProjectWatchdog watchdog;
 
     public ProjectController(CayenneModelerController parent) {
         super(parent);
@@ -282,15 +286,20 @@
         return parent.getView();
     }
 
-    public Project getProject() {
+    public ApplicationProject getProject() {
         return project;
     }
 
-    public void setProject(Project currentProject) {
+    public void setProject(ApplicationProject currentProject) {
         if (this.project != currentProject) // strange enough, this method is called twice
         // during project opening. Not to disturb
         // watchdog extra time, adding this check
         {
+            if (this.project != null) {
+                ((ModelerProjectConfiguration) 
+                        project.getConfiguration()).getGraphRegistry().unregister(this);
+            }
+            
             this.project = currentProject;
             this.projectPreferences = null;
 
@@ -308,6 +317,9 @@
                 }
 
                 watchdog.reconfigure();
+                
+                addDomainListener(((ModelerProjectConfiguration) 
+                        project.getConfiguration()).getGraphRegistry());
             }
         }
     }
@@ -456,7 +468,7 @@
      * Finds a domain containing specified DataNode.
      */
     public DataDomain findDomain(DataNode node) {
-        Collection<DataDomain> domains = ((ApplicationProject) getProject())
+        Collection<DataDomain> domains = (getProject())
                 .getConfiguration()
                 .getDomains();
 
@@ -473,7 +485,7 @@
      * Finds a domain containing specified DataMap.
      */
     public DataDomain findDomain(DataMap map) {
-        Collection<DataDomain> domains = ((ApplicationProject) getProject())
+        Collection<DataDomain> domains = (getProject())
                 .getConfiguration()
                 .getDomains();
 
@@ -643,6 +655,10 @@
     public void addDomainListener(DomainListener listener) {
         listenerList.add(DomainListener.class, listener);
     }
+    
+    public void removeDomainListener(DomainListener listener) {
+        listenerList.remove(DomainListener.class, listener);
+    }
 
     public void addDataNodeDisplayListener(DataNodeDisplayListener listener) {
         listenerList.add(DataNodeDisplayListener.class, listener);
@@ -659,14 +675,26 @@
     public void addDataMapListener(DataMapListener listener) {
         listenerList.add(DataMapListener.class, listener);
     }
+    
+    public void removeDataMapListener(DataMapListener listener) {
+        listenerList.remove(DataMapListener.class, listener);
+    }
 
     public void addDbEntityListener(DbEntityListener listener) {
         listenerList.add(DbEntityListener.class, listener);
     }
+    
+    public void removeDbEntityListener(DbEntityListener listener) {
+        listenerList.remove(DbEntityListener.class, listener);
+    }
 
     public void addObjEntityListener(ObjEntityListener listener) {
         listenerList.add(ObjEntityListener.class, listener);
     }
+    
+    public void removeObjEntityListener(ObjEntityListener listener) {
+        listenerList.remove(ObjEntityListener.class, listener);
+    }
 
     public void addDbEntityDisplayListener(DbEntityDisplayListener listener) {
         listenerList.add(DbEntityDisplayListener.class, listener);
@@ -688,6 +716,10 @@
     public void addDbAttributeListener(DbAttributeListener listener) {
         listenerList.add(DbAttributeListener.class, listener);
     }
+    
+    public void removeDbAttributeListener(DbAttributeListener listener) {
+        listenerList.remove(DbAttributeListener.class, listener);
+    }
 
     public void addDbAttributeDisplayListener(DbAttributeDisplayListener listener) {
         listenerList.add(DbAttributeDisplayListener.class, listener);
@@ -696,6 +728,10 @@
     public void addObjAttributeListener(ObjAttributeListener listener) {
         listenerList.add(ObjAttributeListener.class, listener);
     }
+    
+    public void removeObjAttributeListener(ObjAttributeListener listener) {
+        listenerList.remove(ObjAttributeListener.class, listener);
+    }
 
     public void addObjAttributeDisplayListener(ObjAttributeDisplayListener listener) {
         listenerList.add(ObjAttributeDisplayListener.class, listener);
@@ -704,6 +740,10 @@
     public void addDbRelationshipListener(DbRelationshipListener listener) {
         listenerList.add(DbRelationshipListener.class, listener);
     }
+    
+    public void removeDbRelationshipListener(DbRelationshipListener listener) {
+        listenerList.add(DbRelationshipListener.class, listener);
+    }
 
     public void addDbRelationshipDisplayListener(DbRelationshipDisplayListener listener) {
         listenerList.add(DbRelationshipDisplayListener.class, listener);
@@ -712,6 +752,10 @@
     public void addObjRelationshipListener(ObjRelationshipListener listener) {
         listenerList.add(ObjRelationshipListener.class, listener);
     }
+    
+    public void removeObjRelationshipListener(ObjRelationshipListener listener) {
+        listenerList.remove(ObjRelationshipListener.class, listener);
+    }
 
     public void addObjRelationshipDisplayListener(ObjRelationshipDisplayListener listener) {
         listenerList.add(ObjRelationshipDisplayListener.class, listener);
@@ -1273,7 +1317,7 @@
                 currentState.domain = e.getDomain();
                 currentState.node = e.getDataNode();
                 currentState.map = e.getDataMap();
-                currentState.embeddable = (Embeddable) e.getEmbeddable();
+                currentState.embeddable = e.getEmbeddable();
             }
         }
 
@@ -1497,7 +1541,7 @@
                 clearState();
                 currentState.domain = ev.getDomain();
                 currentState.map = ev.getDataMap();
-                currentState.embeddable = (Embeddable) ev.getEmbeddable();
+                currentState.embeddable = ev.getEmbeddable();
             }
             currentState.embAttrs = new EmbeddableAttribute[ev.getEmbeddableAttributes().length];
             System.arraycopy(

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectConfiguration.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectConfiguration.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectConfiguration.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectConfiguration.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.action;
+
+import java.io.File;
+import java.io.InputStream;
+
+import org.apache.cayenne.modeler.graph.GraphRegistry;
+import org.apache.cayenne.project.ProjectConfiguration;
+
+/**
+ * Modeler project configuration. This class exists for accessing
+ * resources of configuration, which is protected in Configuration class 
+ */
+public class ModelerProjectConfiguration extends ProjectConfiguration {
+    /**
+     * Registry for storing graph builders
+     */
+    GraphRegistry graphRegistry;
+
+    public ModelerProjectConfiguration(File projectFile) {
+        super(projectFile);
+        graphRegistry = new GraphRegistry();
+    }
+
+    @Override
+    protected InputStream getMapConfiguration(String name) {
+        return super.getMapConfiguration(name);
+    }
+    
+    public GraphRegistry getGraphRegistry() {
+        return graphRegistry;
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectLoadDelegate.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectLoadDelegate.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectLoadDelegate.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ModelerProjectLoadDelegate.java Wed Dec  2 14:37:42 2009
@@ -19,6 +19,7 @@
 
 package org.apache.cayenne.modeler.action;
 
+import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -29,7 +30,12 @@
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.conf.Configuration;
 import org.apache.cayenne.conf.RuntimeLoadDelegate;
+import org.apache.cayenne.modeler.graph.GraphFile;
+import org.apache.cayenne.modeler.graph.GraphLoader;
 import org.apache.cayenne.modeler.util.ModelerDbAdapter;
+import org.apache.cayenne.util.Util;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
 
 /**
  * Project loader delegate customized for use in CayenneModeler.
@@ -96,6 +102,26 @@
         for (DataDomain domain : getDomains().values()) {
             config.addDomain(domain);
         }
+        
+        //AFTER everything was loaded, load graphs as well
+        for (DataDomain domain : getDomains().values()) {
+            String location = domain.getName() + GraphFile.LOCATION_SUFFIX;
+            InputStream in = ((ModelerProjectConfiguration) config).getMapConfiguration(location);
+            
+            if (in != null) {
+                try {                    
+                    XMLReader parser = Util.createXmlReader();
+                    GraphLoader handler = new GraphLoader(((ModelerProjectConfiguration) config).
+                            getGraphRegistry().getGraphMap(domain));
+                    parser.setContentHandler(handler);
+                    parser.setErrorHandler(handler);
+                    parser.parse(new InputSource(in));
+                }
+                catch (Exception ex) {
+                    loadError(ex);
+                }
+            }
+        }
     }
 
     /**

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/NewProjectAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/NewProjectAction.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/NewProjectAction.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/NewProjectAction.java Wed Dec  2 14:37:42 2009
@@ -30,9 +30,9 @@
 import org.apache.cayenne.modeler.Application;
 import org.apache.cayenne.modeler.CayenneModelerController;
 import org.apache.cayenne.modeler.event.DomainDisplayEvent;
+import org.apache.cayenne.modeler.util.ModelerUtil;
 import org.apache.cayenne.project.ApplicationProject;
 import org.apache.cayenne.project.NamedObjectFactory;
-import org.apache.cayenne.project.Project;
 
 /**
  */
@@ -65,7 +65,7 @@
         }
 
         Configuration config = buildProjectConfiguration(null);
-        Project project = new ApplicationProject(null, config);
+        ApplicationProject project = ModelerUtil.createModelerProject(null, config, getProjectController());
 
         // stick a DataDomain
         DataDomain domain = (DataDomain) NamedObjectFactory.createObject(

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/OpenProjectAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/OpenProjectAction.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/OpenProjectAction.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/OpenProjectAction.java Wed Dec  2 14:37:42 2009
@@ -30,6 +30,7 @@
 import org.apache.cayenne.conf.Configuration;
 import org.apache.cayenne.modeler.Application;
 import org.apache.cayenne.modeler.dialog.ErrorDebugDialog;
+import org.apache.cayenne.modeler.util.ModelerUtil;
 import org.apache.cayenne.project.ApplicationProject;
 import org.apache.cayenne.project.Project;
 import org.apache.cayenne.project.ProjectException;
@@ -120,7 +121,7 @@
                     file.getAbsolutePath());
 
             Configuration config = buildProjectConfiguration(file);
-            Project project = new ApplicationProject(file, config);
+            ApplicationProject project = ModelerUtil.createModelerProject(file, config, getProjectController());
             getProjectController().setProject(project);
 
             // if upgrade was canceled

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ProjectAction.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ProjectAction.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ProjectAction.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/ProjectAction.java Wed Dec  2 14:37:42 2009
@@ -65,7 +65,7 @@
      * @since 1.2
      */
     protected Configuration buildProjectConfiguration(File projectFile) {
-        ProjectConfiguration config = new ProjectConfiguration(projectFile);
+        ProjectConfiguration config = new ModelerProjectConfiguration(projectFile);
         config.setLoaderDelegate(new ModelerProjectLoadDelegate(config));
         config.setSaverDelegate(new ModelerProjectSaveDelegate(config));
         return config;

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataDomainTabbedView.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataDomainTabbedView.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataDomainTabbedView.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/DataDomainTabbedView.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,71 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.editor;
+
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.modeler.graph.DataDomainGraphTab;
+
+/**
+ * DataDomain editing tabs container 
+ */
+public class DataDomainTabbedView extends JTabbedPane implements ChangeListener {
+    ProjectController mediator;
+    
+    DataDomainGraphTab graphTab;
+
+    /**
+     * constructor
+     * @param mediator mediator instance
+     */
+    public DataDomainTabbedView(ProjectController mediator) {
+        this.mediator = mediator;
+
+        initView();
+    }
+
+    /**
+     * create tabs
+     */
+    private void initView() {
+      
+        setTabPlacement(JTabbedPane.TOP);
+
+        // add panels to tabs
+        // note that those panels that have no internal scrollable tables
+        // must be wrapped in a scroll pane
+        JScrollPane domainView = new JScrollPane(new DataDomainView(mediator));
+        addTab("Main", domainView);
+
+        graphTab = new DataDomainGraphTab(mediator);
+        addTab("Graph", graphTab);
+        
+        addChangeListener(this);
+    }
+
+    public void stateChanged(ChangeEvent e) {
+        if (getSelectedComponent() == graphTab) {
+            graphTab.rebuild();
+        }
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/EditorView.java Wed Dec  2 14:37:42 2009
@@ -134,8 +134,8 @@
 
         detailPanel.add(new JPanel(), EMPTY_VIEW);
 
-        Component domainView = new DataDomainView(eventController);
-        detailPanel.add(new JScrollPane(domainView), DOMAIN_VIEW);
+        Component domainView = new DataDomainTabbedView(eventController);
+        detailPanel.add(domainView, DOMAIN_VIEW);
 
         DataNodeEditor nodeController = new DataNodeEditor(eventController);
         detailPanel.add(nodeController.getView(), NODE_VIEW);

Modified: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbentity/DbEntityRelationshipTab.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbentity/DbEntityRelationshipTab.java?rev=886158&r1=886157&r2=886158&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbentity/DbEntityRelationshipTab.java (original)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/editor/dbentity/DbEntityRelationshipTab.java Wed Dec  2 14:37:42 2009
@@ -44,6 +44,7 @@
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.Entity;
 import org.apache.cayenne.map.event.DbEntityListener;
 import org.apache.cayenne.map.event.DbRelationshipListener;
 import org.apache.cayenne.map.event.EntityEvent;
@@ -293,7 +294,7 @@
         AutoCompletion.enable(targetCombo);
         
         targetCombo.setRenderer(CellRenderers.entityListRendererWithIcons(entity.getDataMap()));
-        targetCombo.setModel(createComboModel());
+        targetCombo.setModel(createComboModel(entity));
         col.setCellEditor(CayenneWidgetFactory.createCellEditor(targetCombo));
         table.getSelectionModel().addListSelectionListener(this);
     }
@@ -346,7 +347,7 @@
         // If this is just loading new currentDbEntity, do nothing
         if (mediator.getCurrentDbEntity() == null)
             return;
-        targetCombo.setModel(createComboModel());
+        targetCombo.setModel(createComboModel(e.getEntity()));
 
         DbRelationshipTableModel model = (DbRelationshipTableModel) table.getModel();
         model.fireTableDataChanged();
@@ -356,8 +357,8 @@
     /**
      * Creates a list of DbEntities.
      */
-    private ComboBoxModel createComboModel() {
-        DataMap map = mediator.getCurrentDataMap();
+    private ComboBoxModel createComboModel(Entity entity) {
+        DataMap map = entity.getDataMap();
         Object[] objects = map.getNamespace().getDbEntities().toArray();
         return new DefaultComboBoxModel(objects);
     }

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/BaseGraphBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/BaseGraphBuilder.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/BaseGraphBuilder.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/BaseGraphBuilder.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,610 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.graph;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Point;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.JPopupMenu;
+import javax.swing.border.LineBorder;
+import javax.swing.event.UndoableEditEvent;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.map.Relationship;
+import org.apache.cayenne.map.event.DataMapEvent;
+import org.apache.cayenne.map.event.DataMapListener;
+import org.apache.cayenne.map.event.EntityEvent;
+import org.apache.cayenne.map.event.RelationshipEvent;
+import org.apache.cayenne.modeler.ActionManager;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.modeler.action.CreateAttributeAction;
+import org.apache.cayenne.modeler.action.CreateRelationshipAction;
+import org.apache.cayenne.modeler.graph.action.EntityDisplayAction;
+import org.apache.cayenne.modeler.graph.action.RemoveEntityAction;
+import org.apache.cayenne.util.XMLEncoder;
+import org.jgraph.JGraph;
+import org.jgraph.graph.DefaultCellViewFactory;
+import org.jgraph.graph.DefaultEdge;
+import org.jgraph.graph.DefaultGraphCell;
+import org.jgraph.graph.DefaultGraphModel;
+import org.jgraph.graph.GraphConstants;
+import org.jgraph.graph.GraphLayoutCache;
+import org.jgraph.graph.GraphModel;
+
+import com.jgraph.layout.JGraphFacade;
+import com.jgraph.layout.organic.JGraphOrganicLayout;
+
+/**
+ * Base class for building graphs of entities
+ */
+abstract class BaseGraphBuilder implements GraphBuilder, DataMapListener {
+    static final Font EDGE_FONT = new Font("Verdana", 0, 10);
+    
+    /**
+     * Graph
+     */
+    protected JGraph graph;
+    
+    /**
+     * Domain
+     */
+    protected transient DataDomain domain;
+    
+    /**
+     * Created entity cells.
+     * Maps to entity name, since GraphBuilder can be serialized
+     */
+    protected Map<String, DefaultGraphCell> entityCells;
+    
+    /**
+     * Created relationship cells
+     * Maps to relationship qualified name, since GraphBuilder can be serialized
+     */
+    protected Map<String, DefaultEdge> relCells;
+    
+    /**
+     * Created non-isolated objects
+     */
+    protected List<DefaultGraphCell> createdObjects;
+        
+    /**
+     * Current project controller
+     */
+    protected transient ProjectController mediator;
+    
+    protected transient Entity selectedEntity;
+    
+    transient JPopupMenu popup;
+    
+    boolean undoEventsDisabled;
+    
+    public synchronized void buildGraph(ProjectController mediator, DataDomain domain, boolean doLayout) {
+        if (graph != null) {
+            //graph already built, exiting silently
+            return;
+        }
+        
+        graph = new JGraph();
+        GraphModel model = new DefaultGraphModel();
+        graph.setModel(model);
+        
+        setProjectController(mediator);
+        setDataDomain(domain);
+        
+        GraphLayoutCache view = new GraphLayoutCache(model, new DefaultCellViewFactory());
+        graph.setGraphLayoutCache(view);
+        
+        graph.addMouseListener(new MouseAdapter() {
+            public void mouseReleased(MouseEvent e) {
+                if (e.isPopupTrigger()) {
+                    Object selected = graph.getSelectionCell();
+                    if (selected != null && selected instanceof DefaultGraphCell) {
+                        Object userObject = ((DefaultGraphCell) selected).getUserObject();
+                        if (userObject instanceof EntityCellMetadata) {
+                            showPopup(e.getPoint(), 
+                                    ((EntityCellMetadata) userObject).fetchEntity());
+                        }
+                    }
+                }
+            }
+        });
+        
+        graph.addMouseWheelListener(new MouseWheelListener() {
+            public void mouseWheelMoved(MouseWheelEvent e) {
+                graph.setScale(graph.getScale() / Math.pow(ZOOM_FACTOR, e.getWheelRotation()));
+            }
+        });
+        
+        entityCells = new HashMap<String, DefaultGraphCell>();
+        createdObjects = new ArrayList<DefaultGraphCell>();
+        relCells = new HashMap<String, DefaultEdge>();
+        
+        /**
+         * an array for entities that are not connected to anyone.
+         * We add them separately so that layout doesn't touch them
+         */
+        List<DefaultGraphCell> isolatedObjects = new ArrayList<DefaultGraphCell>();
+        
+        /**
+         * 1. Add all entities
+         */
+        for (DataMap map : domain.getDataMaps()) {
+            DefaultGraphCell mapCell = new DefaultGraphCell();
+            createdObjects.add(mapCell);
+            
+            for (Entity entity : getEntities(map)) {
+                DefaultGraphCell cell = createEntityCell(entity);
+                
+//                mapCell.add(cell);
+//                cell.setParent(mapCell);
+                
+                List<DefaultGraphCell> array = !isIsolated(domain, entity) ?
+                        createdObjects : isolatedObjects;
+                array.add(cell);
+                array.add((DefaultGraphCell) cell.addPort());
+            }
+        }
+        
+        /**
+         * 2. Add all relationships
+         */        
+        for (DataMap map : domain.getDataMaps()) {
+            for (Entity entity : getEntities(map)) {
+                DefaultGraphCell sourceCell = entityCells.get(entity.getName());
+                
+                postProcessEntity(entity, sourceCell);               
+            }
+        }
+        view.insert(createdObjects.toArray());
+        
+        if (doLayout) {
+            JGraphFacade facade = new JGraphFacade(graph);
+            
+            JGraphOrganicLayout layout = new JGraphOrganicLayout();
+            layout.setNodeDistributionCostFactor(5000000000000.0);
+            layout.setEdgeLengthCostFactor(1000);
+            layout.setEdgeCrossingCostFactor(1000000);
+            layout.setOptimizeBorderLine(false);
+            layout.setOptimizeEdgeDistance(false);
+            
+    //        JGraphSimpleLayout layout = new JGraphSimpleLayout(JGraphSimpleLayout.TYPE_TILT, 4000, 2000);
+            layout.run(facade);
+            Map nested = facade.createNestedMap(true, true); // Obtain a map of the resulting attribute changes from the facade
+            
+            edit(nested); // Apply the results to the actual graph
+        }
+        
+        /**
+         * Adding isolated objects
+         * 
+         * We're placing them so that they will take maximum space in left top corner.
+         * The sample order is below:
+         * 
+         * 1 2 6 7...
+         * 3 5 8 ...
+         * 4 9...
+         * 10 ...
+         */
+        if (isolatedObjects.size() > 0) {
+            int n = isolatedObjects.size() / 2; //number of isolated entities
+            int x = (int) Math.ceil((Math.sqrt(1 + 8*n) - 1) / 2); //side of triangle
+            
+            Dimension pref = graph.getPreferredSize();
+            int dx = pref.width / 2 / x; //x-distance between entities
+            int dy = pref.height / 2 / x; //y-distance between entities
+            
+            int posX = dx / 2;
+            int posY = dy / 2;
+            
+            int row = 0;
+            
+            for (int isolatedIndex = 0; isolatedIndex < isolatedObjects.size();) {
+                for (int i = 0; isolatedIndex < isolatedObjects.size() && i < x - row; i++) {
+                    GraphConstants.setBounds(isolatedObjects.get(isolatedIndex).getAttributes(), 
+                        new Rectangle2D.Double(pref.width - posX, pref.height - 3 * posY / 2, 10, 10));
+                    isolatedIndex += 2; //because every 2nd object is port
+                    posX += dx;
+                }
+                posX = dx / 2;
+                posY += dy / 2;
+                row++;
+            }
+        }
+        
+        view.insert(isolatedObjects.toArray());
+        graph.getModel().addUndoableEditListener(this);
+    }
+
+    protected DefaultGraphCell createEntityCell(Entity entity) {
+        DefaultGraphCell cell = new DefaultGraphCell(getCellMetadata(entity));
+                        
+        GraphConstants.setResize(cell.getAttributes(), true);
+        GraphConstants.setBorder(cell.getAttributes(), new LineBorder(Color.BLACK));
+        
+        GraphConstants.setEditable(cell.getAttributes(), false);
+        entityCells.put(entity.getName(), cell);
+        return cell;
+    }
+    
+    public DefaultGraphCell getEntityCell(String entityName) {
+        return entityCells.get(entityName);
+    }
+    
+    /**
+     * Post (i.e. after creation on entity cell) process of the entity 
+     */
+    protected void postProcessEntity(Entity entity, DefaultGraphCell cell) {
+        for (Relationship rel : entity.getRelationships()) {
+            if (rel.getSourceEntity() != null && rel.getTargetEntity() != null) {
+                DefaultEdge edge = createRelationshipCell(rel);
+                if (edge != null) {
+                    createdObjects.add(edge);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Returns whether an entity is not connected to any other
+     * TODO: not fine algorithm, it iterates through all entities and all rels 
+     */
+    protected boolean isIsolated(DataDomain domain, Entity entity) {
+        if (entity.getRelationships().size() == 0) {
+            //searching for rels that have a target="entity"
+            
+            for (DataMap map : domain.getDataMaps()) {
+                for (Entity source : getEntities(map)) {
+                    if (source.getAnyRelationship(entity) != null) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+    
+    protected abstract Collection<? extends Entity> getEntities(DataMap map);
+    
+    /**
+     * Returns label for relationship on the graph, considering its "mandatory" and "to-many"
+     * properties
+     */
+    private static String getRelationshipLabel(Relationship rel) {
+        if (rel == null) {
+            return null;
+        }
+        
+        if (rel.isToMany()) {
+            return "0..*";
+        }
+        return rel.isMandatory() ? "1" : "0..1";
+    }
+    
+    /**
+     * Returns metadata (user object) for this cell
+     */
+    protected abstract EntityCellMetadata getCellMetadata(Entity e);
+    
+    protected void showPopup(Point p, Entity entity) {
+        selectedEntity = entity;
+        if (popup == null) {
+            popup = createPopupMenu();
+        }
+        popup.show(graph, p.x, p.y);
+    }
+    
+    public Entity getSelectedEntity() {
+        return selectedEntity;
+    }
+    
+    /**
+     * Creates popup menu
+     */
+    protected JPopupMenu createPopupMenu() {
+        ActionManager actionManager = Application.getInstance().getActionManager();
+        
+        JPopupMenu menu = new JPopupMenu();
+        menu.add(new EntityDisplayAction(this).buildMenu());
+        menu.addSeparator();
+        menu.add(new EntityDisplayAction(this, 
+                actionManager.getAction(CreateAttributeAction.getActionName())).buildMenu());
+        menu.add(new EntityDisplayAction(this, 
+                actionManager.getAction(CreateRelationshipAction.getActionName())).buildMenu());
+        menu.addSeparator();
+        menu.add(new RemoveEntityAction(this));
+        
+        return menu;
+    }
+    
+    /**
+     * Updates specified entity on the graph
+     */
+    protected void updateEntityCell(Entity e) {
+        DefaultGraphCell cell = entityCells.get(e.getName());
+        if (cell != null) {
+            GraphConstants.setValue(cell.getAttributes(), getCellMetadata(e));
+            GraphConstants.setResize(cell.getAttributes(), true);
+            
+            Map nested = new HashMap();
+            nested.put(cell, cell.getAttributes());
+            
+            edit(nested);
+        }
+    }
+    
+    protected void updateRelationshipCell(Relationship rel) {
+        if (rel.getSourceEntity() != null && rel.getTargetEntity() != null) {
+            DefaultEdge edge = relCells.get(getQualifiedName(rel));
+            if (edge != null) {
+                updateRelationshipLabels(edge, rel, rel.getReverseRelationship());
+                
+                Map nested = new HashMap();
+                nested.put(edge, edge.getAttributes());
+                edit(nested);
+            }
+            else {
+                insertRelationshipCell(rel);
+            }
+        }
+    }
+    
+    protected void removeEntityCell(Entity e) {
+        final DefaultGraphCell cell = entityCells.get(e.getName());
+        if (cell != null) {
+            runWithUndoDisabled(new Runnable() {
+                public void run() {
+                    graph.getGraphLayoutCache().remove(new Object[] { cell }, true, true);
+                }
+            });
+            entityCells.remove(e.getName());
+        }
+    }
+    
+    protected void removeRelationshipCell(Relationship rel) {
+        final DefaultEdge edge = relCells.get(getQualifiedName(rel));
+        if (edge != null) {
+            runWithUndoDisabled(new Runnable() {
+                public void run() {
+                    graph.getGraphLayoutCache().remove(new Object[] { edge });
+                }
+            });
+            relCells.remove(getQualifiedName(rel));
+        }
+    }
+    
+    protected DefaultEdge createRelationshipCell(Relationship rel) {        
+        if (!relCells.containsKey(getQualifiedName(rel))) {
+            Relationship reverse = rel.getReverseRelationship();
+            
+            DefaultEdge edge = new DefaultEdge();
+            
+//            GraphConstants.setLineStyle(edge.getAttributes(), GraphConstants.STYLE_ORTHOGONAL);
+//            GraphConstants.setRouting(edge.getAttributes(), GraphConstants.ROUTING_SIMPLE);
+            
+            GraphConstants.setEditable(edge.getAttributes(), false);
+            GraphConstants.setLabelAlongEdge(edge.getAttributes(), true);
+            GraphConstants.setSelectable(edge.getAttributes(), false);
+            GraphConstants.setFont(edge.getAttributes(), EDGE_FONT);
+            
+            updateRelationshipLabels(edge, rel, reverse);
+            
+            relCells.put(getQualifiedName(rel), edge);
+            
+            if (reverse != null) {
+                relCells.put(getQualifiedName(reverse), edge);
+            }
+            
+            return edge;
+        }
+        return null;
+    }
+    
+    
+    protected void insertRelationshipCell(Relationship rel) {
+        DefaultEdge edge = createRelationshipCell(rel);
+        insert(edge);
+    }
+    
+    protected void insertEntityCell(Entity entity) {
+        DefaultGraphCell cell = createEntityCell(entity);
+        
+        //putting cell to a random posistion..
+        GraphConstants.setBounds(cell.getAttributes(), 
+            new Rectangle2D.Double(Math.random() * graph.getWidth(), Math.random() * graph.getHeight(), 
+                    10, 10));
+        
+        //setting graph type-specific attrs
+        postProcessEntity(entity, cell);
+        
+        insert(cell);
+    }
+    
+    /**
+     * Updates relationship labels for specified relationship edge.
+     * @param order order of relationship in entity's same target relationships - to differ labels of relationships with same source and target
+     */
+    protected void updateRelationshipLabels(DefaultEdge edge, Relationship rel, Relationship reverse) {
+        DefaultGraphCell sourceCell = entityCells.get(rel.getSourceEntity().getName());
+        DefaultGraphCell targetCell = entityCells.get(rel.getTargetEntity().getName());
+        
+        edge.setSource(sourceCell != null ? sourceCell.getChildAt(0) : null);
+        edge.setTarget(targetCell != null ? targetCell.getChildAt(0) : null);
+        
+        Object[] labels = {
+            rel.getName() + " " + getRelationshipLabel(rel),
+            reverse == null ? 
+                "" : reverse.getName() + " " + getRelationshipLabel(reverse)
+        };
+        GraphConstants.setExtraLabels(edge.getAttributes(), labels);
+        
+        Point2D[] labelPositions = {
+                new Point2D.Double(GraphConstants.PERMILLE * (0.1 + 0.2 * Math.random()), 10),
+                new Point2D.Double(GraphConstants.PERMILLE * (0.9 - 0.2 * Math.random()), -10)
+        };
+        GraphConstants.setExtraLabelPositions(edge.getAttributes(), labelPositions);
+    }
+    
+    protected boolean isInCurrentDomain() {
+        return mediator.getCurrentDataDomain() == domain;
+    }
+    
+    public JGraph getGraph() {
+        return graph;
+    }
+    
+    public void dataMapAdded(DataMapEvent e) {
+    }
+    
+    public void dataMapChanged(DataMapEvent e) {
+    }
+    
+    public void dataMapRemoved(DataMapEvent e) {
+        for (Entity entity : getEntities(e.getDataMap())) {
+            removeEntityCell(entity);
+        }
+    }
+    
+    public void setProjectController(ProjectController mediator) {
+        this.mediator = mediator;
+        
+        mediator.addDataMapListener(this);
+    }
+    
+    public void setDataDomain(DataDomain domain) {
+        this.domain = domain;
+    }
+    
+    public DataDomain getDataDomain() {
+        return domain;
+    }
+    
+    public void destroy() {
+        mediator.removeDataMapListener(this);
+    }
+    
+    /**
+     * Checks if entity name has changed, then changes map key
+     */
+    protected void remapEntity(EntityEvent e) {
+        if (e.isNameChange()) {
+            entityCells.put(e.getNewName(), entityCells.remove(e.getOldName()));
+        }
+    }
+    
+    /**
+     * Checks if entity name has changed, then changes map key
+     */
+    protected void remapRelationship(RelationshipEvent e) {
+        if (e.isNameChange()) {
+            relCells.put(getQualifiedName(e.getRelationship()), 
+                    relCells.remove(e.getEntity().getName() + "." + e.getOldName()));
+        }
+    }
+    
+    /**
+     * Returns qualified name (entity name + relationship name) for a relationship
+     */
+    static String getQualifiedName(Relationship rel) {
+        return rel.getSourceEntity().getName() + "." + rel.getName();
+    }
+    
+    public void encodeAsXML(XMLEncoder encoder) {
+        encoder.print("<graph type=\"");
+        encoder.print(getType().toString());
+        encoder.print("\" scale=\"");
+        encoder.print(String.valueOf(graph.getScale()));
+        encoder.println("\">");
+        encoder.indent(1);
+        
+        for (Entry<String, DefaultGraphCell> entry : entityCells.entrySet()) {
+            encoder.print("<entity name=\"");
+            encoder.print(entry.getKey());
+            encoder.print("\" ");
+            
+            DefaultGraphCell cell = entry.getValue();
+            Rectangle2D rect = graph.getCellBounds(cell);
+            encodeRecangle(encoder, rect);
+            encoder.println("/>");
+        }
+        
+        encoder.indent(-1);
+        encoder.println("</graph>");
+    }
+    
+    private void encodeRecangle(XMLEncoder encoder, Rectangle2D rect) {
+        encoder.print("x=\"");
+        encoder.print(rect.getX() + "\" y=\"");
+        encoder.print(rect.getY() + "\" width=\"");
+        encoder.print(rect.getWidth() + "\" height=\"");
+        encoder.print(rect.getHeight() + "\" ");
+    }
+    
+    private synchronized void edit(final Map map) {
+        runWithUndoDisabled(new Runnable() {
+            public void run() {
+                graph.getGraphLayoutCache().edit(map);
+            }
+        });
+    }
+    
+    private synchronized void insert(final Object cell) {
+        runWithUndoDisabled(new Runnable() {
+            public void run() {
+                graph.getGraphLayoutCache().insert(cell);
+            }
+        });
+    }
+    
+    private synchronized void runWithUndoDisabled(Runnable r) {
+        undoEventsDisabled = true;
+        try {
+            r.run();
+        }
+        finally {
+            undoEventsDisabled = false;
+        }
+    }
+    
+    public void undoableEditHappened(UndoableEditEvent e) {
+        if (!undoEventsDisabled) {
+            //graph has been modified
+            mediator.setDirty(true);
+            
+            Application.getInstance().getUndoManager().undoableEditHappened(e);
+        }
+    }
+}

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DataDomainGraphTab.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DataDomainGraphTab.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DataDomainGraphTab.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DataDomainGraphTab.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,152 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.graph;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JToolBar;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.modeler.Application;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.modeler.action.ModelerProjectConfiguration;
+import org.apache.cayenne.modeler.event.DomainDisplayEvent;
+import org.apache.cayenne.modeler.event.DomainDisplayListener;
+import org.apache.cayenne.modeler.graph.action.RefreshGraphAction;
+import org.apache.cayenne.modeler.graph.action.SaveAsImageAction;
+import org.apache.cayenne.modeler.graph.action.ZoomInAction;
+import org.apache.cayenne.modeler.graph.action.ZoomOutAction;
+import org.apache.cayenne.modeler.util.CayenneWidgetFactory;
+import org.jgraph.JGraph;
+
+/**
+ * Tab for editing graphical representation of a dataDomain
+ */
+public class DataDomainGraphTab extends JPanel implements DomainDisplayListener, ItemListener {    
+    /**
+     * mediator instance
+     */
+    ProjectController mediator;
+    
+    /**
+     * Diagram selection combo
+     */
+    JComboBox diagramCombo;
+    
+    /**
+     * Scrollpane that the graph will be added to
+     */
+    JScrollPane scrollPane;
+    
+    /**
+     * Current graph
+     */
+    JGraph graph;
+    
+    /**
+     * Current domain
+     */
+    DataDomain domain;
+    
+    /**
+     * True to invoke rebuild next time component becomes visible
+     */
+    boolean needRebuild;
+    
+    public DataDomainGraphTab(ProjectController mediator) {
+        this.mediator = mediator;
+        initView();
+    }
+    
+    private void initView() {
+        needRebuild = true;
+        mediator.addDomainDisplayListener(this);
+        
+        setLayout(new BorderLayout());
+        JToolBar toolbar = new JToolBar();
+        toolbar.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 1));
+        
+        GraphType[] types = GraphType.values();
+        String[] names = new String[types.length];
+        for (int i = 0; i < types.length; i++) {
+            names[i] = types[i].getName();
+        }
+        
+        diagramCombo = CayenneWidgetFactory.createComboBox(
+                names,
+                false);
+        diagramCombo.addItemListener(this);
+        
+        toolbar.add(new RefreshGraphAction(this, Application.getInstance()).buildButton());
+        toolbar.add(new SaveAsImageAction(this, Application.getInstance()).buildButton());
+        toolbar.addSeparator();
+        toolbar.add(new ZoomInAction(this, Application.getInstance()).buildButton());
+        toolbar.add(new ZoomOutAction(this, Application.getInstance()).buildButton());
+        
+        toolbar.addSeparator();
+        toolbar.add(new JLabel("Diagram: "));
+        toolbar.add(diagramCombo);
+        add(toolbar, BorderLayout.NORTH);
+        
+        scrollPane = new JScrollPane();
+        add(scrollPane);
+    }
+
+    public void currentDomainChanged(DomainDisplayEvent e) {
+        if (domain != e.getDomain()) {
+            domain = e.getDomain();
+            needRebuild = true;
+            
+            if (isVisible()) {
+                rebuild();
+            }
+        }
+    }
+    
+    /**
+     * Rebuilds graph from a domain
+     */
+    public synchronized void rebuild() {
+        if (needRebuild && domain != null) {
+            ModelerProjectConfiguration conf = (ModelerProjectConfiguration)
+                mediator.getProject().getConfiguration();
+            graph = conf.getGraphRegistry().loadGraph(mediator, domain, 
+                    GraphType.values()[diagramCombo.getSelectedIndex()]);
+            scrollPane.setViewportView(graph);
+            
+            needRebuild = false;
+        }
+    }
+
+    public void itemStateChanged(ItemEvent e) {
+        needRebuild = true;
+        rebuild();
+    }
+    
+    public JGraph getGraph() {
+        return graph;
+    }
+}

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbEntityCellMetadata.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbEntityCellMetadata.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbEntityCellMetadata.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbEntityCellMetadata.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,42 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.graph;
+
+import org.apache.cayenne.map.Attribute;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.Entity;
+
+/**
+ * Descriptor of DbEntity Cell
+ */
+class DbEntityCellMetadata extends EntityCellMetadata {
+    DbEntityCellMetadata(GraphBuilder builder, String entityName) {
+        super(builder, entityName);
+    }
+    
+    @Override
+    public Entity fetchEntity() {
+        return builder.getDataDomain().getEntityResolver().getDbEntity(entityName);
+    }
+
+    @Override
+    protected boolean isPrimary(Attribute attr) {
+        return ((DbAttribute) attr).isPrimaryKey();
+    }
+}

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbGraphBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbGraphBuilder.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbGraphBuilder.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/DbGraphBuilder.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,148 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.graph;
+
+import java.awt.Color;
+import java.util.Collection;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.map.Relationship;
+import org.apache.cayenne.map.event.AttributeEvent;
+import org.apache.cayenne.map.event.DbAttributeListener;
+import org.apache.cayenne.map.event.DbEntityListener;
+import org.apache.cayenne.map.event.DbRelationshipListener;
+import org.apache.cayenne.map.event.EntityEvent;
+import org.apache.cayenne.map.event.RelationshipEvent;
+import org.apache.cayenne.modeler.ProjectController;
+import org.jgraph.graph.DefaultEdge;
+import org.jgraph.graph.DefaultGraphCell;
+import org.jgraph.graph.GraphConstants;
+
+/**
+ * Class for building ER-graph, based on DbEntity information
+ */
+class DbGraphBuilder extends BaseGraphBuilder 
+    implements DbEntityListener, DbAttributeListener, DbRelationshipListener {
+    
+    static final Color ENTITY_COLOR = new Color(197, 253, 252);
+    
+    @Override
+    protected Collection<? extends Entity> getEntities(DataMap map) {
+        return map.getDbEntities();
+    }
+    
+    @Override
+    protected void postProcessEntity(Entity entity, DefaultGraphCell cell) {
+        super.postProcessEntity(entity, cell);
+        
+        GraphConstants.setBackground(cell.getAttributes(), ENTITY_COLOR);
+        GraphConstants.setOpaque(cell.getAttributes(), true);
+    }
+
+    @Override
+    protected EntityCellMetadata getCellMetadata(Entity e) {
+        return new DbEntityCellMetadata(this, e.getName());
+    }
+    
+    @Override
+    protected DefaultEdge createRelationshipCell(Relationship rel) {
+        DefaultEdge edge = super.createRelationshipCell(rel);
+        if (edge != null) {
+            GraphConstants.setDashPattern(edge.getAttributes(), new float[] { 10, 3 });
+        }
+        return edge;
+    }
+        
+    @Override
+    public void setProjectController(ProjectController mediator) {
+        super.setProjectController(mediator);
+        
+        mediator.addDbEntityListener(this);
+        mediator.addDbAttributeListener(this);
+        mediator.addDbRelationshipListener(this);
+    }
+
+    public void destroy() {
+        super.destroy();
+        
+        mediator.removeDbEntityListener(this);
+        mediator.removeDbAttributeListener(this);
+        mediator.removeDbRelationshipListener(this);
+    }
+
+    public void dbEntityAdded(EntityEvent e) {
+        if (isInCurrentDomain()) {
+            insertEntityCell(e.getEntity());
+        }
+    }
+
+    public void dbEntityChanged(EntityEvent e) {
+        if (isInCurrentDomain()) {
+            remapEntity(e);
+            
+            updateEntityCell(e.getEntity());
+        }
+    }
+
+    public void dbEntityRemoved(EntityEvent e) {
+        if (isInCurrentDomain()) {
+            removeEntityCell(e.getEntity());
+        }
+    }
+
+    public void dbAttributeAdded(AttributeEvent e) {
+        if (isInCurrentDomain()) {
+            updateEntityCell(e.getEntity());
+        }
+    }
+
+    public void dbAttributeChanged(AttributeEvent e) {
+        if (isInCurrentDomain()) {
+            updateEntityCell(e.getEntity());
+        }
+    }
+
+    public void dbAttributeRemoved(AttributeEvent e) {
+        if (isInCurrentDomain()) {
+            updateEntityCell(e.getEntity());
+        }
+    }
+
+    public void dbRelationshipAdded(RelationshipEvent e) {
+        //nothing because relationship does not have target yet
+    }
+
+    public void dbRelationshipChanged(RelationshipEvent e) {
+        if (isInCurrentDomain()) {
+            updateRelationshipCell(e.getRelationship());
+        }
+    }
+
+    public void dbRelationshipRemoved(RelationshipEvent e) {
+        if (isInCurrentDomain()) {
+            remapRelationship(e);
+            removeRelationshipCell(e.getRelationship());
+        }
+    }
+
+    public GraphType getType() {
+        return GraphType.ER;
+    }
+}

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/EntityCellMetadata.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/EntityCellMetadata.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/EntityCellMetadata.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/EntityCellMetadata.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,86 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.graph;
+
+import java.io.Serializable;
+
+import org.apache.cayenne.map.Attribute;
+import org.apache.cayenne.map.Entity;
+
+/**
+ * Abstract class to describe entity's cell 
+ */
+abstract class EntityCellMetadata implements Serializable {
+    GraphBuilder builder;
+    
+    String entityName;
+    String label;
+    
+    EntityCellMetadata() {
+    }
+    
+    EntityCellMetadata(GraphBuilder builder, String entityName) {
+        this.builder = builder;
+        this.entityName = entityName;
+        
+        rebuildLabel();
+    }
+        
+    /**
+     * Resolves entity
+     */
+    public abstract Entity fetchEntity();
+    
+    final void rebuildLabel() {
+        label = createLabel();
+    }
+    
+    public String toString() {
+        if (label == null) {
+            rebuildLabel();
+        }
+        
+        return label;
+    }
+    
+    /**
+     * Creates label for this cell
+     */
+    String createLabel() {
+        Entity entity = fetchEntity();
+        StringBuilder label = new StringBuilder("<html><center><u><b>").
+                append(entity.getName()).append("</b></u></center>");
+        for (Attribute attr : entity.getAttributes()) {
+            if (isPrimary(attr)) {
+                label.append("<br><i>").append(attr.getName()).append("</i>");
+            }
+        }
+        for (Attribute attr : entity.getAttributes()) {
+            if (!isPrimary(attr)) {
+                label.append("<br>").append(attr.getName());
+            }
+        }
+        return label.toString();
+    }
+    
+    /**
+     * Returns whether attribute is "primary" and should therefore be written in italic
+     */
+    protected abstract boolean isPrimary(Attribute attr);
+}

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphBuilder.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphBuilder.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphBuilder.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,72 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.graph;
+
+import java.io.Serializable;
+
+import javax.swing.event.UndoableEditListener;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.modeler.ProjectController;
+import org.apache.cayenne.util.XMLSerializable;
+import org.jgraph.JGraph;
+import org.jgraph.graph.DefaultGraphCell;
+
+/**
+ * Interface for building graphs which represent some prespective of a domain
+ */
+public interface GraphBuilder extends Serializable, XMLSerializable, UndoableEditListener {
+    public static final double ZOOM_FACTOR = 1.3;
+    
+    /**
+     * Builds graph
+     */
+    public void buildGraph(ProjectController mediator, DataDomain domain, boolean layout);
+    
+    /**
+     * Invoked at destroying of the builder
+     */
+    public void destroy();
+    
+    /**
+     * Returns built graph for this builder
+     */
+    public JGraph getGraph();
+    
+    /**
+     * Returns domain.
+     */
+    public DataDomain getDataDomain();
+        
+    /**
+     * Returns type of the graph
+     */
+    public GraphType getType();
+    
+    /**
+     * Returns selected entity, <code>null</code> if none is selected
+     */
+    public Entity getSelectedEntity();
+    
+    /**
+     * Returns cell of an entity
+     */
+    public DefaultGraphCell getEntityCell(String entityName);
+}

Added: cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphFile.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphFile.java?rev=886158&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphFile.java (added)
+++ cayenne/main/trunk/framework/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/graph/GraphFile.java Wed Dec  2 14:37:42 2009
@@ -0,0 +1,74 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.modeler.graph;
+
+import java.io.PrintWriter;
+
+import org.apache.cayenne.project.Project;
+import org.apache.cayenne.project.ProjectFile;
+import org.apache.cayenne.util.XMLEncoder;
+
+/**
+ * File, representing graph(s) of a domain
+ */
+public class GraphFile extends ProjectFile {
+    public static final String LOCATION_SUFFIX = ".graph.xml";
+    
+    GraphMap graphMap;
+    
+    public GraphFile(Project project, GraphMap graphMap) {
+        super(project, null);
+        this.graphMap = graphMap;
+    }
+    
+    /**
+     * @see ProjectFile#getObject()
+     */
+    @Override
+    public Object getObject() {
+        return graphMap;
+    }
+    
+    /**
+     * @see ProjectFile#getObjectName()
+     */
+    @Override
+    public String getObjectName() {
+        return graphMap.getDomain().getName();
+    }
+    
+    @Override
+    public String getLocationSuffix() {
+        return LOCATION_SUFFIX;
+    }
+    
+    /**
+     * @see org.apache.cayenne.project.ProjectFile#canHandle(Object)
+     */
+    @Override
+    public boolean canHandle(Object obj) {
+        return obj instanceof GraphMap && ((GraphMap) obj).size() > 0;
+    }
+    
+    @Override
+    public void save(PrintWriter out) throws Exception {
+        out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
+        graphMap.encodeAsXML(new XMLEncoder(out, "\t"));
+    }
+}