You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2016/09/30 18:05:01 UTC

[5/5] cayenne git commit: CAY-2116 Split schema synchronization code in a separate module

CAY-2116 Split schema synchronization code in a separate module

* more refactoring


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

Branch: refs/heads/master
Commit: ad9447556dd5e12b5e7f94a517bab1df7f50a5d3
Parents: 2df9f66
Author: Andrus Adamchik <an...@objectstyle.com>
Authored: Fri Sep 30 20:45:47 2016 +0300
Committer: Andrus Adamchik <an...@objectstyle.com>
Committed: Fri Sep 30 20:58:07 2016 +0300

----------------------------------------------------------------------
 .../dbsync/merge/CreateTableToModel.java        |  24 +-
 .../dbsync/merge/EntityMergeSupport.java        |   8 +-
 .../dbsync/naming/DuplicateNameResolver.java    |  68 ++++++
 .../cayenne/dbsync/naming/NameChecker.java      |  36 +++
 .../cayenne/dbsync/naming/NameCheckers.java     | 223 +++++++++++++++++++
 .../cayenne/dbsync/reverse/db/DbLoader.java     |   8 +-
 .../reverse/db/ManyToManyCandidateEntity.java   |   6 +-
 .../naming/DefaultObjectNameGenerator.java      |  11 +-
 .../naming/LegacyObjectNameGenerator.java       |  16 +-
 .../dbsync/reverse/naming/NameConverter.java    | 154 -------------
 .../cayenne/dbsync/naming/NameCheckersTest.java | 204 +++++++++++++++++
 .../reverse/naming/NameConverterTest.java       | 115 ----------
 .../java/org/apache/cayenne/map/ObjEntity.java  |   2 +-
 .../apache/cayenne/map/naming/NameChecker.java  |  36 ---
 .../apache/cayenne/map/naming/NameCheckers.java | 221 ------------------
 .../cayenne/map/naming/UniqueNameGenerator.java |  91 --------
 .../main/java/org/apache/cayenne/util/Util.java | 126 +++++++++--
 .../cayenne/map/naming/NameCheckersTest.java    | 204 -----------------
 .../java/org/apache/cayenne/util/UtilTest.java  |  38 ++++
 .../cayenne/gen/ClientDataMapArtifact.java      |   3 +-
 .../org/apache/cayenne/gen/DataMapArtifact.java |   4 +-
 .../org/apache/cayenne/gen/DataMapUtils.java    |  30 +--
 .../org/apache/cayenne/gen/StringUtils.java     |  23 +-
 .../org/apache/cayenne/gen/StringUtilsTest.java |  49 ++++
 .../modeler/action/CreateAttributeAction.java   |  10 +-
 .../action/CreateCallbackMethodAction.java      |  15 +-
 .../modeler/action/CreateDataMapAction.java     |   6 +-
 .../modeler/action/CreateDbEntityAction.java    |   6 +-
 .../modeler/action/CreateEmbeddableAction.java  |   6 +-
 .../modeler/action/CreateNodeAction.java        |   6 +-
 .../modeler/action/CreateObjEntityAction.java   |  12 +-
 .../modeler/action/CreateProcedureAction.java   |   6 +-
 .../action/CreateProcedureParameterAction.java  |   6 +-
 .../action/CreateRelationshipAction.java        |   8 +-
 .../modeler/action/ImportDataMapAction.java     |   8 +-
 .../modeler/action/ImportEOModelAction.java     |   6 +-
 .../modeler/action/NewProjectAction.java        |   6 +-
 .../cayenne/modeler/action/PasteAction.java     |  40 ++--
 .../action/ReverseEngineeringAction.java        |   8 +-
 .../dialog/ResolveDbRelationshipDialog.java     |   8 +-
 .../modeler/dialog/db/DbLoaderHelper.java       |  10 +-
 .../cayenne/modeler/dialog/query/QueryType.java |   6 +-
 .../cayenne/modeler/editor/CallbackType.java    |  32 +--
 modeler/cayenne-wocompat/pom.xml                |   6 +
 .../cayenne/wocompat/EOModelProcessor.java      |  39 ++--
 45 files changed, 933 insertions(+), 1017 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
index 0997e9b..436a1f5 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
@@ -19,21 +19,22 @@
 package org.apache.cayenne.dbsync.merge;
 
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.naming.DuplicateNameResolver;
+import org.apache.cayenne.dbsync.naming.NameCheckers;
+import org.apache.cayenne.dbsync.reverse.naming.DefaultObjectNameGenerator;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.dbsync.reverse.naming.NameConverter;
 
 /**
  * A {@link MergerToken} to add a {@link DbEntity} to a {@link DataMap}
- * 
  */
 public class CreateTableToModel extends AbstractToModelToken.Entity {
 
     /**
      * className if {@link ObjEntity} should be generated with a
-     *  special class name.
-     * Setting this to <code>null</code>, because by default class name should be generated 
+     * special class name.
+     * Setting this to <code>null</code>, because by default class name should be generated
      */
     private String objEntityClassName = null; //CayenneDataObject.class.getName();
 
@@ -53,11 +54,18 @@ public class CreateTableToModel extends AbstractToModelToken.Entity {
     }
 
     public void execute(MergerContext mergerContext) {
+        DbEntity dbEntity = getEntity();
+
         DataMap map = mergerContext.getDataMap();
-        map.addDbEntity(getEntity());
+        map.addDbEntity(dbEntity);
 
         // create a ObjEntity
-        String objEntityName = NameConverter.underscoredToJava(getEntity().getName(), true);
+
+        // TODO: proper name generator must be injected
+
+        String objEntityName = new DefaultObjectNameGenerator().createObjEntityName(dbEntity);
+        objEntityName = DuplicateNameResolver.resolve(NameCheckers.objEntity, dbEntity.getDataMap(), objEntityName);
+
         // this loop will terminate even if no valid name is found
         // to prevent loader from looping forever (though such case is very unlikely)
         String baseName = objEntityName;
@@ -77,12 +85,12 @@ public class CreateTableToModel extends AbstractToModelToken.Entity {
 
         objEntity.setClassName(className);
         objEntity.setSuperClassName(map.getDefaultSuperclass());
-        
+
         if (map.isClientSupported()) {
             objEntity.setClientClassName(map.getNameWithDefaultClientPackage(objEntity.getName()));
             objEntity.setClientSuperClassName(map.getDefaultClientSuperclass());
         }
-        
+
         map.addObjEntity(objEntity);
 
         // presumably there are no other ObjEntities pointing to this DbEntity, so syncing just this one...

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
index 7db68af..e055883 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
@@ -29,9 +29,9 @@ import org.apache.cayenne.map.Entity;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
-import org.apache.cayenne.map.naming.UniqueNameGenerator;
+import org.apache.cayenne.dbsync.naming.DuplicateNameResolver;
 import org.apache.cayenne.dbsync.reverse.naming.LegacyObjectNameGenerator;
-import org.apache.cayenne.map.naming.NameCheckers;
+import org.apache.cayenne.dbsync.naming.NameCheckers;
 import org.apache.cayenne.dbsync.reverse.naming.ObjectNameGenerator;
 import org.apache.cayenne.util.DeleteRuleUpdater;
 import org.apache.cayenne.util.EntityMergeListener;
@@ -205,7 +205,7 @@ public class EntityMergeSupport {
 
     private boolean createObjRelationship(ObjEntity entity, DbRelationship dr, String targetEntityName) {
         String relationshipName = nameGenerator.createObjRelationshipName(dr);
-        relationshipName = UniqueNameGenerator.generate(NameCheckers.objRelationship, entity, relationshipName);
+        relationshipName = DuplicateNameResolver.resolve(NameCheckers.objRelationship, entity, relationshipName);
 
         ObjRelationship or = new ObjRelationship(relationshipName);
         or.addDbRelationship(dr);
@@ -279,7 +279,7 @@ public class EntityMergeSupport {
     }
 
     private void addMissingAttribute(ObjEntity entity, DbAttribute da) {
-        String attrName = UniqueNameGenerator.generate(NameCheckers.objAttribute, entity,
+        String attrName = DuplicateNameResolver.resolve(NameCheckers.objAttribute, entity,
                 nameGenerator.createObjAttributeName(da));
 
         String type = TypesMapping.getJavaBySqlType(da.getType());

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/DuplicateNameResolver.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/DuplicateNameResolver.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/DuplicateNameResolver.java
new file mode 100644
index 0000000..3f55d8f
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/DuplicateNameResolver.java
@@ -0,0 +1,68 @@
+/*****************************************************************
+ *   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.dbsync.naming;
+
+import org.apache.cayenne.map.DataMap;
+
+/**
+ * A generator of unique names for the various model objects.
+ *
+ * @since 4.0
+ */
+public class DuplicateNameResolver {
+
+    private static final String DEFAULT_PATTERN = "%s%d";
+
+    public static String resolve(NameChecker checker) {
+        return resolve(checker, DEFAULT_PATTERN, null, null);
+    }
+
+    public static String resolve(NameChecker checker, Object context) {
+        return resolve(checker, DEFAULT_PATTERN, context, null);
+    }
+
+    public static String resolve(NameChecker checker, Object context, String baseName) {
+        return resolve(checker, DEFAULT_PATTERN, context, baseName);
+    }
+
+    public static String resolve(NameChecker nameChecker, String pattern, Object context, String baseName) {
+
+        if (baseName == null) {
+            baseName = nameChecker.baseName();
+        }
+
+        String resolved = doResolve(nameChecker, pattern, context, baseName);
+
+        // TODO ugly hack with cast... something more OO is in order
+        return (nameChecker == NameCheckers.embeddable)
+                ? ((DataMap) context).getNameWithDefaultPackage(resolved) : resolved;
+    }
+
+
+    private static String doResolve(NameChecker nameChecker, String pattern, Object namingContext, String baseName) {
+        int c = 1;
+        String name = baseName;
+        while (nameChecker.isNameInUse(namingContext, name)) {
+            name = String.format(pattern, baseName, c++);
+        }
+
+        return name;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameChecker.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameChecker.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameChecker.java
new file mode 100644
index 0000000..ade1ec5
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameChecker.java
@@ -0,0 +1,36 @@
+/*****************************************************************
+ *   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.dbsync.naming;
+
+/**
+ * @since 4.0
+ */
+public interface NameChecker {
+
+	/**
+	 * Returns a base default name, like "UntitledEntity", etc.
+	 */
+	String baseName();
+
+	/**
+	 * Checks if the name is already taken by another sibling in the same
+	 * context.
+	 */
+	boolean isNameInUse(Object namingContext, String name);
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameCheckers.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameCheckers.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameCheckers.java
new file mode 100644
index 0000000..fca1734
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/naming/NameCheckers.java
@@ -0,0 +1,223 @@
+/*****************************************************************
+ *   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.dbsync.naming;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.configuration.DataNodeDescriptor;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.Embeddable;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.Procedure;
+import org.apache.cayenne.map.ProcedureParameter;
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * A set of default {@link NameChecker} objects for the known model objects.
+ *
+ * @since 4.0
+ */
+public enum NameCheckers implements NameChecker {
+
+    dataChannelDescriptor("project") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            return false;
+        }
+    },
+
+    dataMap("datamap") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            // null context is a situation when DataMap is a
+            // top level object of the project
+            if (namingContext == null) {
+                return false;
+            }
+
+            if (namingContext instanceof DataDomain) {
+                DataDomain domain = (DataDomain) namingContext;
+                return domain.getDataMap(name) != null;
+            }
+
+            if (namingContext instanceof DataChannelDescriptor) {
+                DataChannelDescriptor domain = (DataChannelDescriptor) namingContext;
+                return domain.getDataMap(name) != null;
+            }
+            return false;
+        }
+    },
+
+    reverseEngineering("reverseEngineering") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            if (namingContext == null) {
+                return false;
+            }
+
+            for (DataMap dataMap : ((DataChannelDescriptor) namingContext).getDataMaps()) {
+                if (dataMap != null && dataMap.getReverseEngineering() != null &&
+                        dataMap.getReverseEngineering().getName() != null && dataMap.getReverseEngineering().getName().equals(name)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    },
+
+    objEntity("ObjEntity") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            DataMap map = (DataMap) namingContext;
+            return map.getObjEntity(name) != null;
+        }
+    },
+
+    embeddable("Embeddable") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            DataMap map = (DataMap) namingContext;
+            return map.getEmbeddable(map.getNameWithDefaultPackage(name)) != null;
+        }
+    },
+
+    embeddableAttribute("untitledAttr") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            Embeddable emb = (Embeddable) namingContext;
+            return emb.getAttribute(name) != null;
+        }
+    },
+
+    dbEntity("db_entity") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            DataMap map = (DataMap) namingContext;
+            return map.getDbEntity(name) != null;
+        }
+    },
+
+    procedureParameter("UntitledProcedureParameter") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+
+            // it doesn't matter if we create a parameter with
+            // a duplicate name.. parameters are positional anyway..
+            // still try to use unique names for visual consistency
+            Procedure procedure = (Procedure) namingContext;
+            for (final ProcedureParameter parameter : procedure
+                    .getCallParameters()) {
+                if (name.equals(parameter.getName())) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    },
+
+    procedure("procedure") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            DataMap map = (DataMap) namingContext;
+            return map.getProcedure(name) != null;
+        }
+    },
+
+    query("query") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            DataMap map = (DataMap) namingContext;
+            return map.getQueryDescriptor(name) != null;
+        }
+    },
+
+    objAttribute("untitledAttr") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            return objRelationship.isNameInUse(namingContext, name);
+        }
+    },
+
+    dbAttribute("untitledAttr") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            Entity ent = (Entity) namingContext;
+            return ent.getAttribute(name) != null
+                    || ent.getRelationship(name) != null;
+        }
+    },
+
+    dataNodeDescriptor("datanode") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            DataChannelDescriptor domain = (DataChannelDescriptor) namingContext;
+            for (DataNodeDescriptor dataNodeDescriptor : domain
+                    .getNodeDescriptors()) {
+                if (dataNodeDescriptor.getName().equals(name)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    },
+
+    objRelationship("untitledRel") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            ObjEntity ent = (ObjEntity) namingContext;
+            return dbAttribute.isNameInUse(namingContext, name)
+                    || ent.getCallbackMethods().contains(
+                    "get" + StringUtils.capitalize(name));
+        }
+    },
+
+    dbRelationship("untitledRel") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            return dbAttribute.isNameInUse(namingContext, name);
+        }
+    },
+
+    objCallbackMethod("ObjCallbackMethod") {
+        @Override
+        public boolean isNameInUse(Object namingContext, String name) {
+            ObjEntity ent = (ObjEntity) namingContext;
+
+            return name.startsWith("get")
+                    && dbAttribute.isNameInUse(namingContext,
+                    StringUtils.uncapitalize(name.substring(3)))
+                    || ent.getCallbackMethods().contains(name);
+        }
+    };
+
+    private final String baseName;
+
+    NameCheckers(String baseName) {
+        this.baseName = baseName;
+    }
+
+    @Override
+    public String baseName() {
+        return baseName;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
index 5f233cd..44b9fb3 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
@@ -34,9 +34,9 @@ import org.apache.cayenne.map.DbRelationshipDetected;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.Procedure;
 import org.apache.cayenne.map.ProcedureParameter;
-import org.apache.cayenne.map.naming.UniqueNameGenerator;
+import org.apache.cayenne.dbsync.naming.DuplicateNameResolver;
 import org.apache.cayenne.dbsync.reverse.naming.LegacyObjectNameGenerator;
-import org.apache.cayenne.map.naming.NameCheckers;
+import org.apache.cayenne.dbsync.naming.NameCheckers;
 import org.apache.cayenne.dbsync.reverse.naming.ObjectNameGenerator;
 import org.apache.cayenne.util.EqualsBuilder;
 import org.apache.commons.logging.Log;
@@ -133,7 +133,7 @@ public class DbLoader {
                 continue;
             }
 
-            String objEntityName = UniqueNameGenerator.generate(NameCheckers.objEntity, map,
+            String objEntityName = DuplicateNameResolver.resolve(NameCheckers.objEntity, map,
                     nameGenerator.createObjEntityName(dbEntity));
 
             ObjEntity objEntity = new ObjEntity(objEntityName);
@@ -439,7 +439,7 @@ public class DbLoader {
 
     private String generateName(DbEntity entity, ExportedKey key, boolean toMany) {
         String forwardPreferredName = nameGenerator.createDbRelationshipName(key, toMany);
-        return UniqueNameGenerator.generate(NameCheckers.dbRelationship, entity, forwardPreferredName);
+        return DuplicateNameResolver.resolve(NameCheckers.dbRelationship, entity, forwardPreferredName);
     }
 
     private void fireObjEntitiesAddedEvents(Collection<ObjEntity> loadedObjEntities) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java
index 06c7d82..85af6a4 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java
@@ -21,8 +21,8 @@ package org.apache.cayenne.dbsync.reverse.db;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
-import org.apache.cayenne.map.naming.UniqueNameGenerator;
-import org.apache.cayenne.map.naming.NameCheckers;
+import org.apache.cayenne.dbsync.naming.DuplicateNameResolver;
+import org.apache.cayenne.dbsync.naming.NameCheckers;
 import org.apache.cayenne.dbsync.reverse.naming.ObjectNameGenerator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -113,7 +113,7 @@ class ManyToManyCandidateEntity {
                 (short) 1);
 
         ObjRelationship newRelationship = new ObjRelationship();
-        newRelationship.setName(UniqueNameGenerator.generate(NameCheckers.objRelationship, srcEntity,
+        newRelationship.setName(DuplicateNameResolver.resolve(NameCheckers.objRelationship, srcEntity,
                 nameGenerator.createDbRelationshipName(key, true)));
 
         newRelationship.setSourceEntity(srcEntity);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/DefaultObjectNameGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/DefaultObjectNameGenerator.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/DefaultObjectNameGenerator.java
index d299338..a2d2264 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/DefaultObjectNameGenerator.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/DefaultObjectNameGenerator.java
@@ -18,10 +18,11 @@
  ****************************************************************/
 package org.apache.cayenne.dbsync.reverse.naming;
 
+import org.apache.cayenne.dbsync.reverse.db.ExportedKey;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.dbsync.reverse.db.ExportedKey;
+import org.apache.cayenne.util.Util;
 import org.jvnet.inflector.Noun;
 
 import java.util.Locale;
@@ -73,21 +74,21 @@ public class DefaultObjectNameGenerator implements ObjectNameGenerator {
 			}
 		}
 
-		return NameConverter.underscoredToJava(name, false);
+		return Util.underscoredToJava(name, false);
 	}
 
 	@Override
 	public String createObjEntityName(DbEntity dbEntity) {
-		return NameConverter.underscoredToJava(dbEntity.getName(), true);
+		return Util.underscoredToJava(dbEntity.getName(), true);
 	}
 
 	@Override
 	public String createObjAttributeName(DbAttribute attr) {
-		return NameConverter.underscoredToJava(attr.getName(), false);
+		return Util.underscoredToJava(attr.getName(), false);
 	}
 
 	@Override
 	public String createObjRelationshipName(DbRelationship dbRel) {
-		return NameConverter.underscoredToJava(dbRel.getName(), false);
+		return Util.underscoredToJava(dbRel.getName(), false);
 	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/LegacyObjectNameGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/LegacyObjectNameGenerator.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/LegacyObjectNameGenerator.java
index 16f7f7a..1c52a6e 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/LegacyObjectNameGenerator.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/LegacyObjectNameGenerator.java
@@ -18,35 +18,37 @@
  ****************************************************************/
 package org.apache.cayenne.dbsync.reverse.naming;
 
+import org.apache.cayenne.dbsync.reverse.db.ExportedKey;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.dbsync.reverse.db.ExportedKey;
+import org.apache.cayenne.util.Util;
 
 /**
  * BasicNamingStrategy is an naming strategy that creates names in Cayenne's
  * old-fashioned manner, i.e. the same way Cayenne did before 3.0
- * 
+ *
  * @since 4.0
  */
 public class LegacyObjectNameGenerator implements ObjectNameGenerator {
+
     public String createDbRelationshipName(
             ExportedKey key,
             boolean toMany) {
-        
+
         String uglyName = (toMany) ? key.getFKTableName() + "_ARRAY" : "to_" + key.getPKTableName();
-        return NameConverter.underscoredToJava(uglyName, false);
+        return Util.underscoredToJava(uglyName, false);
     }
 
     public String createObjEntityName(DbEntity dbEntity) {
-        return NameConverter.underscoredToJava(dbEntity.getName(), true);
+        return Util.underscoredToJava(dbEntity.getName(), true);
     }
 
     public String createObjAttributeName(DbAttribute attr) {
-        return NameConverter.underscoredToJava(attr.getName(), false);
+        return Util.underscoredToJava(attr.getName(), false);
     }
 
     public String createObjRelationshipName(DbRelationship dbRel) {
-        return NameConverter.underscoredToJava(dbRel.getName(), false);
+        return Util.underscoredToJava(dbRel.getName(), false);
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/NameConverter.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/NameConverter.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/NameConverter.java
deleted file mode 100644
index 63c4dcb..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/naming/NameConverter.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*****************************************************************
- *   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.dbsync.reverse.naming;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-/**
- * Utility class to convert from different naming styles to Java convention. For example
- * names like "ABCD_EFG" can be converted to "abcdEfg".
- */
-// TODO: deprecate
-public class NameConverter {
-
-    private static final Map<String, String> SPECIAL_CHAR_TO_JAVA_MAPPING = new HashMap<>();
-
-    static {
-        SPECIAL_CHAR_TO_JAVA_MAPPING.put("#", "pound");
-    }
-
-    /**
-     * Converts a String name to a String following java convention for the static final
-     * variables. E.g. "abcXyz" will be converted to "ABC_XYZ".
-     */
-    // TODO: move to the only user....
-    public static String javaToUnderscored(String name) {
-        if (name == null) {
-            return null;
-        }
-
-        // clear of non-java chars. While the method name implies that a passed identifier
-        // is pure Java, it is used to build pk columns names and such, so extra safety
-        // check is a good idea
-        name = specialCharsToJava(name);
-
-        char charArray[] = name.toCharArray();
-        StringBuilder buffer = new StringBuilder();
-
-        for (int i = 0; i < charArray.length; i++) {
-            if ((Character.isUpperCase(charArray[i])) && (i != 0)) {
-
-                char prevChar = charArray[i - 1];
-                if ((Character.isLowerCase(prevChar))) {
-                    buffer.append("_");
-                }
-            }
-
-            buffer.append(Character.toUpperCase(charArray[i]));
-        }
-
-        return buffer.toString();
-    }
-
-    /**
-     * Converts names like "ABCD_EFG_123" to Java-style names like "abcdEfg123". If
-     * <code>capitalize</code> is true, returned name is capitalized (for instance if
-     * this is a class name).
-     *
-     * @since 1.2
-     */
-    // TODO: migrate users to ObjectNameGenerator
-    public static String underscoredToJava(String name, boolean capitalize) {
-        StringTokenizer st = new StringTokenizer(name, "_");
-        StringBuilder buf = new StringBuilder();
-
-        boolean first = true;
-        while (st.hasMoreTokens()) {
-            String token = st.nextToken();
-
-            // clear of non-java chars
-            token = specialCharsToJava(token);
-
-            int len = token.length();
-            if (len == 0) {
-                continue;
-            }
-
-            // sniff mixed case vs. single case styles
-            boolean hasLowerCase = false;
-            boolean hasUpperCase = false;
-            for (int i = 0; i < len && !(hasUpperCase && hasLowerCase); i++) {
-                if (Character.isUpperCase(token.charAt(i))) {
-                    hasUpperCase = true;
-                } else if (Character.isLowerCase(token.charAt(i))) {
-                    hasLowerCase = true;
-                }
-            }
-
-            // if mixed case, preserve it, if all upper, convert to lower
-            if (hasUpperCase && !hasLowerCase) {
-                token = token.toLowerCase();
-            }
-
-            if (first) {
-                // apply explicit capitalization rules, if this is the first token
-                first = false;
-                if (capitalize) {
-                    buf.append(Character.toUpperCase(token.charAt(0)));
-                } else {
-                    buf.append(Character.toLowerCase(token.charAt(0)));
-                }
-            } else {
-                buf.append(Character.toUpperCase(token.charAt(0)));
-            }
-
-            if (len > 1) {
-                buf.append(token.substring(1, len));
-            }
-        }
-        return buf.toString();
-    }
-
-    /**
-     * Replaces special chars with human-readable and Java-id-compatible symbols.
-     */
-    static String specialCharsToJava(String string) {
-        int len = string.length();
-        if (len == 0) {
-            return string;
-        }
-
-        StringBuilder buffer = new StringBuilder(len);
-        for (int i = 0; i < len; i++) {
-
-            char c = string.charAt(i);
-            if (Character.isJavaIdentifierPart(c)) {
-                buffer.append(c);
-            } else {
-                Object word = SPECIAL_CHAR_TO_JAVA_MAPPING.get(String.valueOf(c));
-                buffer.append(word != null ? word : "_");
-            }
-        }
-
-        return buffer.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/naming/NameCheckersTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/naming/NameCheckersTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/naming/NameCheckersTest.java
new file mode 100644
index 0000000..8131160
--- /dev/null
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/naming/NameCheckersTest.java
@@ -0,0 +1,204 @@
+/*****************************************************************
+ *   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.dbsync.naming;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.configuration.DataNodeDescriptor;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.Embeddable;
+import org.apache.cayenne.map.EmbeddableAttribute;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.Procedure;
+import org.apache.cayenne.map.ProcedureParameter;
+import org.apache.cayenne.map.QueryDescriptor;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class NameCheckersTest {
+
+    @Test
+    public void testObjEntityAttributes() throws Exception {
+        NameCheckers maker = NameCheckers.objAttribute;
+        ObjEntity namingContainer = new ObjEntity();
+
+        String baseName = maker.baseName();
+        String name = DuplicateNameResolver.resolve(maker, namingContainer);
+        assertEquals(baseName, name);
+        namingContainer.addAttribute(new ObjAttribute(name));
+
+        name = DuplicateNameResolver.resolve(maker, namingContainer);
+        assertEquals(baseName + "1", name);
+        namingContainer.addAttribute(new ObjAttribute(name));
+
+        name = DuplicateNameResolver.resolve(maker, namingContainer);
+        assertEquals(baseName + "2", name);
+        namingContainer.addAttribute(new ObjAttribute(name));
+
+        name = DuplicateNameResolver.resolve(maker, namingContainer);
+        assertEquals(baseName + "3", name);
+        namingContainer.addAttribute(new ObjAttribute(name));
+
+        maker = NameCheckers.objRelationship;
+        baseName = maker.baseName();
+        name = DuplicateNameResolver.resolve(maker, namingContainer);
+        assertEquals(baseName, name);
+        namingContainer.addRelationship(new ObjRelationship(name));
+
+        name = DuplicateNameResolver.resolve(maker, namingContainer);
+        assertEquals(baseName + "1", name);
+        namingContainer.addRelationship(new ObjRelationship(name));
+
+        maker = NameCheckers.objCallbackMethod;
+        baseName = maker.baseName();
+        name = DuplicateNameResolver.resolve(maker, namingContainer);
+        assertEquals(baseName, name);
+        namingContainer.addRelationship(new ObjRelationship(name));
+    }
+
+    @Test
+    public void testEntity () {
+        DataMap map = new DataMap();
+
+        map.addDbEntity(new DbEntity("name"));
+        checkNameAndOther(map, NameCheckers.dbEntity, "name");
+
+        map.addObjEntity(new ObjEntity("name"));
+        checkNameAndOther(map, NameCheckers.objEntity, "name");
+
+        map.addProcedure(new Procedure("name"));
+        checkNameAndOther(map, NameCheckers.procedure, "name");
+
+        QueryDescriptor query = QueryDescriptor.selectQueryDescriptor();
+        query.setName("name");
+        map.addQueryDescriptor(query);
+        checkNameAndOther(map, NameCheckers.query, "name");
+    }
+
+    @Test
+    public void testProject() throws Exception {
+        assertFalse(NameCheckers.dataChannelDescriptor.isNameInUse(null, null));
+    }
+
+    @Test
+    public void testDbEntity() throws Exception {
+        DbEntity dbEntity = new DbEntity();
+
+        dbEntity.addRelationship(new DbRelationship("name"));
+        checkNameAndOther(dbEntity, NameCheckers.dbRelationship, "name");
+    }
+
+    @Test
+    public void testProcedureAttr() throws Exception {
+        Procedure procedure = new Procedure();
+
+        procedure.addCallParameter(new ProcedureParameter("name"));
+        checkNameAndOther(procedure, NameCheckers.procedureParameter, "name");
+    }
+
+    @Test
+    public void testEmbeddableAttr() throws Exception {
+        Embeddable embeddable = new Embeddable();
+
+        embeddable.addAttribute(new EmbeddableAttribute("name"));
+        checkNameAndOther(embeddable, NameCheckers.embeddableAttribute, "name");
+    }
+
+    @Test
+    public void testDatanode() throws Exception {
+        DataChannelDescriptor descriptor = new DataChannelDescriptor();
+
+        descriptor.getDataMaps().add(new DataMap("name"));
+        checkNameAndOther(descriptor, NameCheckers.dataMap, "name");
+
+        descriptor.getNodeDescriptors().add(new DataNodeDescriptor("name"));
+        checkNameAndOther(descriptor, NameCheckers.dataNodeDescriptor, "name");
+    }
+
+    @Test
+    public void testDataMap() throws Exception {
+        DataDomain dataDomain = new DataDomain("name");
+
+        dataDomain.addDataMap(new DataMap("name"));
+        checkNameAndOther(dataDomain, NameCheckers.dataMap, "name");
+
+        assertFalse(NameCheckers.dataMap.isNameInUse(null, "name"));
+        assertFalse(NameCheckers.dataMap.isNameInUse(1, "name"));
+    }
+
+    private void checkNameAndOther(Object namingContainer, NameCheckers maker, String newName) {
+        assertTrue(maker.isNameInUse(namingContainer, newName));
+        assertEquals(newName + "1", DuplicateNameResolver.resolve(maker,namingContainer, newName));
+        assertEquals("other" + newName, DuplicateNameResolver.resolve(maker,namingContainer, "other" + newName));
+    }
+
+    @Test
+    public void testOverlappingAttributeAndCallbackNames() throws Exception {
+        ObjEntity namingContainer = new ObjEntity();
+
+        namingContainer.addAttribute(new ObjAttribute("myName"));
+        assertEquals("getMyName1", DuplicateNameResolver.resolve(NameCheckers.objCallbackMethod, namingContainer, "getMyName"));
+
+        namingContainer.getCallbackMap().getPostAdd().addCallbackMethod("getSecondName");
+        assertEquals("SecondName1", DuplicateNameResolver.resolve(NameCheckers.objAttribute, namingContainer, "SecondName"));
+        assertEquals("secondName1", DuplicateNameResolver.resolve(NameCheckers.objAttribute, namingContainer, "secondName"));
+        assertEquals("SecondName1", DuplicateNameResolver.resolve(NameCheckers.objRelationship, namingContainer, "SecondName"));
+        assertEquals("secondName1", DuplicateNameResolver.resolve(NameCheckers.objRelationship, namingContainer, "secondName"));
+    }
+
+    @Test
+    public void testAttributeDifferentInFirstLetterCases() throws Exception {
+        ObjEntity namingContainer = new ObjEntity();
+
+        namingContainer.addAttribute(new ObjAttribute("myName"));
+        Assert.assertTrue(NameCheckers.objAttribute.isNameInUse(namingContainer, "myName"));
+        Assert.assertFalse(NameCheckers.objAttribute.isNameInUse(namingContainer, "MyName"));
+
+        namingContainer.getCallbackMap().getPostAdd().addCallbackMethod("getSecondName");
+        assertEquals("SecondName1", DuplicateNameResolver.resolve(NameCheckers.objAttribute, namingContainer, "SecondName"));
+        assertEquals("secondName1", DuplicateNameResolver.resolve(NameCheckers.objAttribute, namingContainer, "secondName"));
+    }
+
+    @Test
+    public void testEmbeddable() {
+        DataMap map = new DataMap();
+
+        map.addEmbeddable(new Embeddable("name"));
+        Assert.assertTrue(NameCheckers.embeddable.isNameInUse(map, "name"));
+        assertEquals("name1", DuplicateNameResolver.resolve(NameCheckers.embeddable, map, "name"));
+        Assert.assertFalse(NameCheckers.embeddable.isNameInUse(map, "other-name"));
+
+        map.setDefaultPackage("package");
+        Assert.assertFalse(NameCheckers.embeddable.isNameInUse(map, "name"));
+        assertEquals("package.name", DuplicateNameResolver.resolve(NameCheckers.embeddable, map, "name"));
+        map.addEmbeddable(new Embeddable("package.name"));
+
+        Assert.assertTrue(NameCheckers.embeddable.isNameInUse(map, "name"));
+        assertEquals("package.name1", DuplicateNameResolver.resolve(NameCheckers.embeddable, map, "name"));
+        Assert.assertFalse(NameCheckers.embeddable.isNameInUse(map, "other-name"));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/naming/NameConverterTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/naming/NameConverterTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/naming/NameConverterTest.java
deleted file mode 100644
index 1c52f6e..0000000
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/naming/NameConverterTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*****************************************************************
- *   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.dbsync.reverse.naming;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class NameConverterTest {
-
-    @Test
-    public void testUnderscoredToJava1() throws Exception {
-        String expected = "ClassNameIdentifier";
-        assertEquals(expected, NameConverter.underscoredToJava(
-                "_CLASS_NAME_IDENTIFIER_",
-                true));
-    }
-
-    @Test
-    public void testUnderscoredToJava2() throws Exception {
-        String expected = "propNameIdentifier123";
-        assertEquals(expected, NameConverter.underscoredToJava(
-                "_prop_name_Identifier_123",
-                false));
-    }
-
-    @Test
-    public void testUnderscoredToJava3() throws Exception {
-        String expected = "lastName";
-        assertEquals(expected, NameConverter.underscoredToJava("lastName", false));
-    }
-
-    @Test
-    public void testUnderscoredToJava4() throws Exception {
-        String expected = "lastName";
-        assertEquals(expected, NameConverter.underscoredToJava("LastName", false));
-    }
-
-    @Test
-    public void testUnderscoredToJava5() throws Exception {
-        String expected = "LastName";
-        assertEquals(expected, NameConverter.underscoredToJava("LastName", true));
-    }
-
-    @Test
-    public void testUnderscoredToJavaSpecialChars() throws Exception {
-        assertEquals("ABCpoundXyz", NameConverter.underscoredToJava("ABC#_XYZ", true));
-    }
-
-    @Test
-    public void testJavaToUnderscored1() throws Exception {
-        String expected = "LAST_NAME";
-        assertEquals(expected, NameConverter.javaToUnderscored("LastName"));
-    }
-
-    @Test
-    public void testJavaToUnderscored2() throws Exception {
-        String expected = "A_CLASS";
-        assertEquals(expected, NameConverter.javaToUnderscored("aClass"));
-    }
-
-    @Test
-    public void testJavaToUnderscored3() throws Exception {
-        String expected = "VAR_A";
-        assertEquals(expected, NameConverter.javaToUnderscored("varA"));
-    }
-
-    @Test
-    public void testJavaToUnderscored4() throws Exception {
-        String expected = "LAST_NAME";
-        assertEquals(expected, NameConverter.javaToUnderscored("LAST_NAME"));
-    }
-
-    @Test
-    public void testJavaToUnderscored5() throws Exception {
-        String expected = "ABC_A";
-        assertEquals(expected, NameConverter.javaToUnderscored("abc_A"));
-    }
-
-    @Test
-    public void testJavaToUnderscored6() throws Exception {
-        String expected = "A123";
-        assertEquals(expected, NameConverter.javaToUnderscored("a123"));
-    }
-
-    @Test
-    public void testJavaToUnderscored7() throws Exception {
-        String expected = "AB_CDEF";
-        assertEquals(expected, NameConverter.javaToUnderscored("abCDEF"));
-    }
-
-    @Test
-    public void testJavaToUnderscored8() throws Exception {
-        String expected = "AB_CE";
-        assertEquals(expected, NameConverter.javaToUnderscored("abCe"));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
index de79a6a..81a66ee 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
@@ -677,7 +677,7 @@ public class ObjEntity extends Entity implements ObjEntityListener, Configuratio
 
             // create synthetic attribute
             if (attribute == null) {
-                attribute = new SyntheticPKObjAttribute(pk.getName());
+                attribute = new SyntheticPKObjAttribute(Util.underscoredToJava(pk.getName(), false));
                 attribute.setDbAttributePath(pk.getName());
                 attribute.setType(TypesMapping.getJavaBySqlType(pk.getType()));
             }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameChecker.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameChecker.java b/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameChecker.java
deleted file mode 100644
index 68fdfd3..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameChecker.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*****************************************************************
- *   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.map.naming;
-
-/**
- * @since 4.0
- */
-public interface NameChecker {
-
-	/**
-	 * Returns a base default name, like "UntitledEntity", etc.
-	 */
-	String baseName();
-
-	/**
-	 * Checks if the name is already taken by another sibling in the same
-	 * context.
-	 */
-	boolean isNameInUse(Object namingContext, String name);
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java b/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java
deleted file mode 100644
index 67639d6..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/NameCheckers.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*****************************************************************
- *   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.map.naming;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.Embeddable;
-import org.apache.cayenne.map.Entity;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.map.ProcedureParameter;
-import org.apache.commons.lang.StringUtils;
-
-/**
- * @since 4.0
- */
-public enum NameCheckers implements NameChecker {
-
-	dataChannelDescriptor("project") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			return false;
-		}
-	},
-
-	dataMap("datamap") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			// null context is a situation when DataMap is a
-			// top level object of the project
-			if (namingContext == null) {
-				return false;
-			}
-
-			if (namingContext instanceof DataDomain) {
-				DataDomain domain = (DataDomain) namingContext;
-				return domain.getDataMap(name) != null;
-			}
-
-			if (namingContext instanceof DataChannelDescriptor) {
-				DataChannelDescriptor domain = (DataChannelDescriptor) namingContext;
-				return domain.getDataMap(name) != null;
-			}
-			return false;
-		}
-	},
-
-	reverseEngineering("reverseEngineering") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			if (namingContext == null) {
-				return false;
-			}
-
-			for (DataMap dataMap : ((DataChannelDescriptor) namingContext).getDataMaps()) {
-                if (dataMap!= null && dataMap.getReverseEngineering() != null &&
-                        dataMap.getReverseEngineering().getName()!=null && dataMap.getReverseEngineering().getName().equals(name)) {
-                    return true;
-                }
-			}
-			return false;
-		}
-	},
-
-	objEntity("ObjEntity") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			DataMap map = (DataMap) namingContext;
-			return map.getObjEntity(name) != null;
-		}
-	},
-
-	embeddable("Embeddable") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			DataMap map = (DataMap) namingContext;
-			return map.getEmbeddable(map.getNameWithDefaultPackage(name)) != null;
-		}
-	},
-
-	embeddableAttribute("untitledAttr") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			Embeddable emb = (Embeddable) namingContext;
-			return emb.getAttribute(name) != null;
-		}
-	},
-
-	dbEntity("db_entity") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			DataMap map = (DataMap) namingContext;
-			return map.getDbEntity(name) != null;
-		}
-	},
-
-	procedureParameter("UntitledProcedureParameter") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-
-			// it doesn't matter if we create a parameter with
-			// a duplicate name.. parameters are positional anyway..
-			// still try to use unique names for visual consistency
-			Procedure procedure = (Procedure) namingContext;
-			for (final ProcedureParameter parameter : procedure
-					.getCallParameters()) {
-				if (name.equals(parameter.getName())) {
-					return true;
-				}
-			}
-
-			return false;
-		}
-	},
-
-	procedure("procedure") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			DataMap map = (DataMap) namingContext;
-			return map.getProcedure(name) != null;
-		}
-	},
-
-	query("query") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			DataMap map = (DataMap) namingContext;
-			return map.getQueryDescriptor(name) != null;
-		}
-	},
-
-	objAttribute("untitledAttr") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			return objRelationship.isNameInUse(namingContext, name);
-		}
-	},
-
-	dbAttribute("untitledAttr") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			Entity ent = (Entity) namingContext;
-			return ent.getAttribute(name) != null
-					|| ent.getRelationship(name) != null;
-		}
-	},
-
-	dataNodeDescriptor("datanode") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			DataChannelDescriptor domain = (DataChannelDescriptor) namingContext;
-			for (DataNodeDescriptor dataNodeDescriptor : domain
-					.getNodeDescriptors()) {
-				if (dataNodeDescriptor.getName().equals(name)) {
-					return true;
-				}
-			}
-			return false;
-		}
-	},
-
-	objRelationship("untitledRel") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			ObjEntity ent = (ObjEntity) namingContext;
-			return dbAttribute.isNameInUse(namingContext, name)
-					|| ent.getCallbackMethods().contains(
-					"get" + StringUtils.capitalize(name));
-		}
-	},
-
-	dbRelationship("untitledRel") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			return dbAttribute.isNameInUse(namingContext, name);
-		}
-	},
-
-	objCallbackMethod("ObjCallbackMethod") {
-		@Override
-		public boolean isNameInUse(Object namingContext, String name) {
-			ObjEntity ent = (ObjEntity) namingContext;
-
-			return name.startsWith("get")
-					&& dbAttribute.isNameInUse(namingContext,
-					StringUtils.uncapitalize(name.substring(3)))
-					|| ent.getCallbackMethods().contains(name);
-		}
-	};
-
-	public final String baseName;
-
-	NameCheckers(String baseName) {
-		this.baseName = baseName;
-	}
-
-	@Override
-	public String baseName() {
-		return baseName;
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-server/src/main/java/org/apache/cayenne/map/naming/UniqueNameGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/UniqueNameGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/map/naming/UniqueNameGenerator.java
deleted file mode 100644
index 2fe6801..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/naming/UniqueNameGenerator.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*****************************************************************
- *   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.map.naming;
-
-import org.apache.cayenne.map.DataMap;
-
-/**
- * @since 4.0
- */
-public class UniqueNameGenerator {
-
-    public static final String DEFAULT_PATTERN = "%s%d";
-
-    private final NameChecker nameChecker;
-
-    private final String pattern;
-
-    public UniqueNameGenerator(NameChecker nameChecker, String pattern) {
-        this.nameChecker = nameChecker;
-        this.pattern = pattern;
-    }
-
-    public static String generate(NameChecker checker) {
-        return generate(checker, DEFAULT_PATTERN, null, null);
-    }
-
-    public static String generate(NameChecker checker, Object context) {
-        return generate(checker, DEFAULT_PATTERN, context, null);
-    }
-
-    public static String generate(NameChecker checker, Object context, String baseName) {
-        return generate(checker, DEFAULT_PATTERN, context, baseName);
-    }
-
-    public static String generate(NameChecker checker, String pattern, Object context, String baseName) {
-        UniqueNameGenerator generator;
-        if (checker == NameCheckers.embeddable) {
-            generator = new UniqueNameGenerator(NameCheckers.embeddable, pattern) {
-                @Override
-                public String generate(Object namingContext, String nameBase) {
-                    return ((DataMap) namingContext).getNameWithDefaultPackage(super.generate(namingContext, nameBase));
-                }
-            };
-        } else {
-            generator = new UniqueNameGenerator(checker, pattern);
-        }
-
-        return generator.generate(context, baseName);
-    }
-
-    /**
-     * Creates a unique name for the new object and constructs this object.
-     */
-    String generate(Object namingContext) {
-        return generate(namingContext, nameChecker.baseName());
-    }
-
-    String generate(Object namingContext, String nameBase) {
-        return generate(pattern, namingContext, nameBase != null ? nameBase : nameChecker.baseName());
-    }
-
-    /**
-     * @since 1.0.5
-     */
-    private String generate(String pattern, Object namingContext, String nameBase) {
-        int c = 1;
-        String name = nameBase;
-        while (nameChecker.isNameInUse(namingContext, name)) {
-            name = String.format(pattern, nameBase, c++);
-        }
-
-        return name;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java b/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
index e0f5571..14e5eab 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
@@ -19,6 +19,23 @@
 
 package org.apache.cayenne.util;
 
+import org.apache.cayenne.Cayenne;
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.di.AdhocObjectFactory;
+import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
+import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
+import org.apache.cayenne.reflect.ArcProperty;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -42,31 +59,19 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.StringTokenizer;
 import java.util.regex.Pattern;
 
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.apache.cayenne.Cayenne;
-import org.apache.cayenne.PersistenceState;
-import org.apache.cayenne.Persistent;
-import org.apache.cayenne.di.AdhocObjectFactory;
-import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
-import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
-import org.apache.cayenne.reflect.ArcProperty;
-import org.apache.cayenne.reflect.AttributeProperty;
-import org.apache.cayenne.reflect.PropertyVisitor;
-import org.apache.cayenne.reflect.ToManyProperty;
-import org.apache.cayenne.reflect.ToOneProperty;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-
 /**
  * Contains various unorganized static utility methods used across Cayenne.
  */
 public class Util {
 
+	private static final Map<String, String> SPECIAL_CHAR_TO_JAVA_MAPPING = new HashMap<>();
+	static {
+		SPECIAL_CHAR_TO_JAVA_MAPPING.put("#", "pound");
+	}
+
 	@Deprecated
 	private static DefaultAdhocObjectFactory objectFactory;
 
@@ -508,6 +513,91 @@ public class Util {
 		return objectFactory.getJavaClass(className);
 	}
 
+	/**
+	 * Converts names like "ABCD_EFG_123" to Java-style names like "abcdEfg123". If
+	 * <code>capitalize</code> is true, returned name is capitalized (for instance if
+	 * this is a class name).
+	 *
+	 * @since 4.0
+	 */
+	// TODO: trace direct users and switch over to ObjectNameGenerator
+	public static String underscoredToJava(String name, boolean capitalize) {
+		StringTokenizer st = new StringTokenizer(name, "_");
+		StringBuilder buf = new StringBuilder();
+
+		boolean first = true;
+		while (st.hasMoreTokens()) {
+			String token = st.nextToken();
+
+			// clear of non-java chars
+			token = specialCharsToJava(token);
+
+			int len = token.length();
+			if (len == 0) {
+				continue;
+			}
+
+			// sniff mixed case vs. single case styles
+			boolean hasLowerCase = false;
+			boolean hasUpperCase = false;
+			for (int i = 0; i < len && !(hasUpperCase && hasLowerCase); i++) {
+				if (Character.isUpperCase(token.charAt(i))) {
+					hasUpperCase = true;
+				} else if (Character.isLowerCase(token.charAt(i))) {
+					hasLowerCase = true;
+				}
+			}
+
+			// if mixed case, preserve it, if all upper, convert to lower
+			if (hasUpperCase && !hasLowerCase) {
+				token = token.toLowerCase();
+			}
+
+			if (first) {
+				// apply explicit capitalization rules, if this is the first token
+				first = false;
+				if (capitalize) {
+					buf.append(Character.toUpperCase(token.charAt(0)));
+				} else {
+					buf.append(Character.toLowerCase(token.charAt(0)));
+				}
+			} else {
+				buf.append(Character.toUpperCase(token.charAt(0)));
+			}
+
+			if (len > 1) {
+				buf.append(token.substring(1, len));
+			}
+		}
+		return buf.toString();
+	}
+
+	/**
+	 * Replaces special chars with human-readable and Java-id-compatible symbols.
+	 *
+	 * @since 4.0
+	 */
+	public static String specialCharsToJava(String string) {
+		int len = string.length();
+		if (len == 0) {
+			return string;
+		}
+
+		StringBuilder buffer = new StringBuilder(len);
+		for (int i = 0; i < len; i++) {
+
+			char c = string.charAt(i);
+			if (Character.isJavaIdentifierPart(c)) {
+				buffer.append(c);
+			} else {
+				Object word = SPECIAL_CHAR_TO_JAVA_MAPPING.get(String.valueOf(c));
+				buffer.append(word != null ? word : "_");
+			}
+		}
+
+		return buffer.toString();
+	}
+
 	static void setReverse(final Persistent sourceObject, String propertyName, final Persistent targetObject) {
 
 		ArcProperty property = (ArcProperty) Cayenne.getClassDescriptor(sourceObject).getProperty(propertyName);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-server/src/test/java/org/apache/cayenne/map/naming/NameCheckersTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/map/naming/NameCheckersTest.java b/cayenne-server/src/test/java/org/apache/cayenne/map/naming/NameCheckersTest.java
deleted file mode 100644
index e3e6628..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/map/naming/NameCheckersTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*****************************************************************
- *   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.map.naming;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.map.Embeddable;
-import org.apache.cayenne.map.EmbeddableAttribute;
-import org.apache.cayenne.map.ObjAttribute;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.ObjRelationship;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.map.ProcedureParameter;
-import org.apache.cayenne.map.QueryDescriptor;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class NameCheckersTest {
-
-    @Test
-    public void testObjEntityAttributes() throws Exception {
-        NameCheckers maker = NameCheckers.objAttribute;
-        ObjEntity namingContainer = new ObjEntity();
-
-        String baseName = maker.baseName();
-        String name = UniqueNameGenerator.generate(maker, namingContainer);
-        assertEquals(baseName, name);
-        namingContainer.addAttribute(new ObjAttribute(name));
-
-        name = UniqueNameGenerator.generate(maker, namingContainer);
-        assertEquals(baseName + "1", name);
-        namingContainer.addAttribute(new ObjAttribute(name));
-
-        name = UniqueNameGenerator.generate(maker, namingContainer);
-        assertEquals(baseName + "2", name);
-        namingContainer.addAttribute(new ObjAttribute(name));
-
-        name = UniqueNameGenerator.generate(maker, namingContainer);
-        assertEquals(baseName + "3", name);
-        namingContainer.addAttribute(new ObjAttribute(name));
-
-        maker = NameCheckers.objRelationship;
-        baseName = maker.baseName();
-        name = UniqueNameGenerator.generate(maker, namingContainer);
-        assertEquals(baseName, name);
-        namingContainer.addRelationship(new ObjRelationship(name));
-
-        name = UniqueNameGenerator.generate(maker, namingContainer);
-        assertEquals(baseName + "1", name);
-        namingContainer.addRelationship(new ObjRelationship(name));
-
-        maker = NameCheckers.objCallbackMethod;
-        baseName = maker.baseName();
-        name = UniqueNameGenerator.generate(maker, namingContainer);
-        assertEquals(baseName, name);
-        namingContainer.addRelationship(new ObjRelationship(name));
-    }
-
-    @Test
-    public void testEntity () {
-        DataMap map = new DataMap();
-
-        map.addDbEntity(new DbEntity("name"));
-        checkNameAndOther(map, NameCheckers.dbEntity, "name");
-
-        map.addObjEntity(new ObjEntity("name"));
-        checkNameAndOther(map, NameCheckers.objEntity, "name");
-
-        map.addProcedure(new Procedure("name"));
-        checkNameAndOther(map, NameCheckers.procedure, "name");
-
-        QueryDescriptor query = QueryDescriptor.selectQueryDescriptor();
-        query.setName("name");
-        map.addQueryDescriptor(query);
-        checkNameAndOther(map, NameCheckers.query, "name");
-    }
-
-    @Test
-    public void testProject() throws Exception {
-        assertFalse(NameCheckers.dataChannelDescriptor.isNameInUse(null, null));
-    }
-
-    @Test
-    public void testDbEntity() throws Exception {
-        DbEntity dbEntity = new DbEntity();
-
-        dbEntity.addRelationship(new DbRelationship("name"));
-        checkNameAndOther(dbEntity, NameCheckers.dbRelationship, "name");
-    }
-
-    @Test
-    public void testProcedureAttr() throws Exception {
-        Procedure procedure = new Procedure();
-
-        procedure.addCallParameter(new ProcedureParameter("name"));
-        checkNameAndOther(procedure, NameCheckers.procedureParameter, "name");
-    }
-
-    @Test
-    public void testEmbeddableAttr() throws Exception {
-        Embeddable embeddable = new Embeddable();
-
-        embeddable.addAttribute(new EmbeddableAttribute("name"));
-        checkNameAndOther(embeddable, NameCheckers.embeddableAttribute, "name");
-    }
-
-    @Test
-    public void testDatanode() throws Exception {
-        DataChannelDescriptor descriptor = new DataChannelDescriptor();
-
-        descriptor.getDataMaps().add(new DataMap("name"));
-        checkNameAndOther(descriptor, NameCheckers.dataMap, "name");
-
-        descriptor.getNodeDescriptors().add(new DataNodeDescriptor("name"));
-        checkNameAndOther(descriptor, NameCheckers.dataNodeDescriptor, "name");
-    }
-
-    @Test
-    public void testDataMap() throws Exception {
-        DataDomain dataDomain = new DataDomain("name");
-
-        dataDomain.addDataMap(new DataMap("name"));
-        checkNameAndOther(dataDomain, NameCheckers.dataMap, "name");
-
-        assertFalse(NameCheckers.dataMap.isNameInUse(null, "name"));
-        assertFalse(NameCheckers.dataMap.isNameInUse(1, "name"));
-    }
-
-    private void checkNameAndOther(Object namingContainer, NameCheckers maker, String newName) {
-        assertTrue(maker.isNameInUse(namingContainer, newName));
-        assertEquals(newName + "1", UniqueNameGenerator.generate(maker,namingContainer, newName));
-        assertEquals("other" + newName, UniqueNameGenerator.generate(maker,namingContainer, "other" + newName));
-    }
-
-    @Test
-    public void testOverlappingAttributeAndCallbackNames() throws Exception {
-        ObjEntity namingContainer = new ObjEntity();
-
-        namingContainer.addAttribute(new ObjAttribute("myName"));
-        Assert.assertEquals("getMyName1", UniqueNameGenerator.generate(NameCheckers.objCallbackMethod, namingContainer, "getMyName"));
-
-        namingContainer.getCallbackMap().getPostAdd().addCallbackMethod("getSecondName");
-        Assert.assertEquals("SecondName1", UniqueNameGenerator.generate(NameCheckers.objAttribute, namingContainer, "SecondName"));
-        Assert.assertEquals("secondName1", UniqueNameGenerator.generate(NameCheckers.objAttribute, namingContainer, "secondName"));
-        Assert.assertEquals("SecondName1", UniqueNameGenerator.generate(NameCheckers.objRelationship, namingContainer, "SecondName"));
-        Assert.assertEquals("secondName1", UniqueNameGenerator.generate(NameCheckers.objRelationship, namingContainer, "secondName"));
-    }
-
-    @Test
-    public void testAttributeDifferentInFirstLetterCases() throws Exception {
-        ObjEntity namingContainer = new ObjEntity();
-
-        namingContainer.addAttribute(new ObjAttribute("myName"));
-        Assert.assertTrue(NameCheckers.objAttribute.isNameInUse(namingContainer, "myName"));
-        Assert.assertFalse(NameCheckers.objAttribute.isNameInUse(namingContainer, "MyName"));
-
-        namingContainer.getCallbackMap().getPostAdd().addCallbackMethod("getSecondName");
-        Assert.assertEquals("SecondName1", UniqueNameGenerator.generate(NameCheckers.objAttribute, namingContainer, "SecondName"));
-        Assert.assertEquals("secondName1", UniqueNameGenerator.generate(NameCheckers.objAttribute, namingContainer, "secondName"));
-    }
-
-    @Test
-    public void testEmbeddable() {
-        DataMap map = new DataMap();
-
-        map.addEmbeddable(new Embeddable("name"));
-        Assert.assertTrue(NameCheckers.embeddable.isNameInUse(map, "name"));
-        Assert.assertEquals("name1", UniqueNameGenerator.generate(NameCheckers.embeddable, map, "name"));
-        Assert.assertFalse(NameCheckers.embeddable.isNameInUse(map, "other-name"));
-
-        map.setDefaultPackage("package");
-        Assert.assertFalse(NameCheckers.embeddable.isNameInUse(map, "name"));
-        Assert.assertEquals("package.name", UniqueNameGenerator.generate(NameCheckers.embeddable, map, "name"));
-        map.addEmbeddable(new Embeddable("package.name"));
-
-        Assert.assertTrue(NameCheckers.embeddable.isNameInUse(map, "name"));
-        Assert.assertEquals("package.name1", UniqueNameGenerator.generate(NameCheckers.embeddable, map, "name"));
-        Assert.assertFalse(NameCheckers.embeddable.isNameInUse(map, "other-name"));
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-server/src/test/java/org/apache/cayenne/util/UtilTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/util/UtilTest.java b/cayenne-server/src/test/java/org/apache/cayenne/util/UtilTest.java
index 2db6987..eab7371 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/util/UtilTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/util/UtilTest.java
@@ -295,4 +295,42 @@ public class UtilTest {
 		assertEquals("1...78", Util.prettyTrim("12345678", 6));
 	}
 
+	@Test
+	public void testUnderscoredToJava1() throws Exception {
+		String expected = "ClassNameIdentifier";
+		assertEquals(expected, Util.underscoredToJava(
+				"_CLASS_NAME_IDENTIFIER_",
+				true));
+	}
+
+	@Test
+	public void testUnderscoredToJava2() throws Exception {
+		String expected = "propNameIdentifier123";
+		assertEquals(expected, Util.underscoredToJava(
+				"_prop_name_Identifier_123",
+				false));
+	}
+
+	@Test
+	public void testUnderscoredToJava3() throws Exception {
+		String expected = "lastName";
+		assertEquals(expected, Util.underscoredToJava("lastName", false));
+	}
+
+	@Test
+	public void testUnderscoredToJava4() throws Exception {
+		String expected = "lastName";
+		assertEquals(expected, Util.underscoredToJava("LastName", false));
+	}
+
+	@Test
+	public void testUnderscoredToJava5() throws Exception {
+		String expected = "LastName";
+		assertEquals(expected, Util.underscoredToJava("LastName", true));
+	}
+
+	@Test
+	public void testUnderscoredToJavaSpecialChars() throws Exception {
+		assertEquals("ABCpoundXyz", Util.underscoredToJava("ABC#_XYZ", true));
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientDataMapArtifact.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientDataMapArtifact.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientDataMapArtifact.java
index d99a577..a9d1dfe 100644
--- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientDataMapArtifact.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClientDataMapArtifact.java
@@ -19,7 +19,6 @@
 
 package org.apache.cayenne.gen;
 
-import org.apache.cayenne.dbsync.reverse.naming.NameConverter;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.QueryDescriptor;
 import org.apache.cayenne.util.Util;
@@ -46,6 +45,6 @@ public class ClientDataMapArtifact extends DataMapArtifact {
             clientPrefix = "Client_";
         }
 
-        return dataMap.getNameWithDefaultClientPackage(NameConverter.underscoredToJava(clientPrefix + dataMap.getName(), true));
+        return dataMap.getNameWithDefaultClientPackage(Util.underscoredToJava(clientPrefix + dataMap.getName(), true));
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java
index e01d002..8e60495 100644
--- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapArtifact.java
@@ -19,9 +19,9 @@
 
 package org.apache.cayenne.gen;
 
-import org.apache.cayenne.dbsync.reverse.naming.NameConverter;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.QueryDescriptor;
+import org.apache.cayenne.util.Util;
 import org.apache.velocity.VelocityContext;
 
 import java.util.Collection;
@@ -58,7 +58,7 @@ public class DataMapArtifact implements Artifact {
     }
 
     public String getQualifiedClassName() {
-        return dataMap.getNameWithDefaultPackage(NameConverter.underscoredToJava(dataMap.getName(), true));
+        return dataMap.getNameWithDefaultPackage(Util.underscoredToJava(dataMap.getName(), true));
     }
 
     public Object getObject() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapUtils.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapUtils.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapUtils.java
index 9d5c75a..a0013cc 100644
--- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapUtils.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/DataMapUtils.java
@@ -19,17 +19,6 @@
 
 package org.apache.cayenne.gen;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionException;
 import org.apache.cayenne.exp.ExpressionParameter;
@@ -42,11 +31,22 @@ import org.apache.cayenne.map.ObjRelationship;
 import org.apache.cayenne.map.PathComponent;
 import org.apache.cayenne.map.QueryDescriptor;
 import org.apache.cayenne.map.SelectQueryDescriptor;
-import org.apache.cayenne.dbsync.reverse.naming.NameConverter;
 import org.apache.cayenne.query.Ordering;
 import org.apache.cayenne.util.CayenneMapEntry;
+import org.apache.cayenne.util.Util;
 import org.apache.commons.collections.set.ListOrderedSet;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * Attributes and Methods for working with Queries.
  *
@@ -64,7 +64,7 @@ public class DataMapUtils {
 	 * @return Method name that perform query.
 	 */
 	public String getQueryMethodName(QueryDescriptor query) {
-		return NameConverter.underscoredToJava(query.getName(), true);
+		return Util.underscoredToJava(query.getName(), true);
 	}
 
 	/**
@@ -136,7 +136,7 @@ public class DataMapUtils {
 		Matcher matcher = pattern.matcher(qualifierString);
 		while (matcher.find()) {
 			String name = matcher.group();
-			result.add(NameConverter.underscoredToJava(name.substring(1), false));
+			result.add(Util.underscoredToJava(name.substring(1), false));
 		}
 
 		return result;
@@ -209,7 +209,7 @@ public class DataMapUtils {
 			}
 
 			for (String name : names) {
-				types.put(NameConverter.underscoredToJava(name, false), typeName);
+				types.put(Util.underscoredToJava(name, false), typeName);
 			}
 
 			return types;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ad944755/cayenne-tools/src/main/java/org/apache/cayenne/gen/StringUtils.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/gen/StringUtils.java b/cayenne-tools/src/main/java/org/apache/cayenne/gen/StringUtils.java
index 34ccfb3..5473142 100644
--- a/cayenne-tools/src/main/java/org/apache/cayenne/gen/StringUtils.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/gen/StringUtils.java
@@ -20,7 +20,6 @@
 package org.apache.cayenne.gen;
 
 import org.apache.cayenne.project.validation.NameValidationHelper;
-import org.apache.cayenne.dbsync.reverse.naming.NameConverter;
 import org.apache.cayenne.util.Util;
 
 /**
@@ -112,7 +111,27 @@ public class StringUtils {
         if (name == null || name.length() == 0)
             return name;
 
-        return NameConverter.javaToUnderscored(name);
+        // clear of non-java chars. While the method name implies that a passed identifier
+        // is pure Java, it is used to build pk columns names and such, so extra safety
+        // check is a good idea
+        name = Util.specialCharsToJava(name);
+
+        char charArray[] = name.toCharArray();
+        StringBuilder buffer = new StringBuilder();
+
+        for (int i = 0; i < charArray.length; i++) {
+            if ((Character.isUpperCase(charArray[i])) && (i != 0)) {
+
+                char prevChar = charArray[i - 1];
+                if ((Character.isLowerCase(prevChar))) {
+                    buffer.append("_");
+                }
+            }
+
+            buffer.append(Character.toUpperCase(charArray[i]));
+        }
+
+        return buffer.toString();
     }
 
     /**