http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java
----------------------------------------------------------------------
diff --git 
a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java 
b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java
index ce0c1c9..6f2f881 100644
--- 
a/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java
+++ 
b/cayenne-tools/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java
@@ -18,6 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.gen;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.access.loader.NamePatternMatcher;
 import org.apache.cayenne.map.DataMap;
@@ -31,567 +41,544 @@ import org.apache.velocity.app.VelocityEngine;
 import org.apache.velocity.runtime.RuntimeConstants;
 import org.apache.velocity.runtime.log.NullLogSystem;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
 public class ClassGenerationAction {
-    static final String TEMPLATES_DIR_NAME = "templates/v1_2/";
-
-    public static final String SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + 
"singleclass.vm";
-    public static final String SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + 
"subclass.vm";
-    public static final String SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + 
"superclass.vm";
-
-    public static final String EMBEDDABLE_SINGLE_CLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "embeddable-singleclass.vm";
-    public static final String EMBEDDABLE_SUBCLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "embeddable-subclass.vm";
-    public static final String EMBEDDABLE_SUPERCLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "embeddable-superclass.vm";
-
-    public static final String DATAMAP_SINGLE_CLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "datamap-singleclass.vm";
-    public static final String DATAMAP_SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME 
+ "datamap-subclass.vm";
-    public static final String DATAMAP_SUPERCLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "datamap-superclass.vm";
-
-    public static final String SUPERCLASS_PREFIX = "_";
-    private static final String WILDCARD = "*";
-
-    protected Collection<Artifact> artifacts;
-
-    protected String superPkg;
-    protected DataMap dataMap;
-
-    protected ArtifactsGenerationMode artifactsGenerationMode;
-    protected boolean makePairs;
-
-    protected Log logger;
-    protected File destDir;
-    protected boolean overwrite;
-    protected boolean usePkgPath;
-
-    protected String template;
-    protected String superTemplate;
-    protected String embeddableTemplate;
-    protected String embeddableSuperTemplate;
-    protected String queryTemplate;
-    protected String querySuperTemplate;
-    protected long timestamp;
-    protected String outputPattern;
-    protected String encoding;
-    protected boolean createPropertyNames;
-
-    // runtime ivars
-    protected VelocityContext context;
-    protected Map<String, Template> templateCache;
-
-    public ClassGenerationAction() {
-        this.outputPattern = "*.java";
-        this.timestamp = System.currentTimeMillis();
-        this.usePkgPath = true;
-        this.makePairs = true;
-        this.context = new VelocityContext();
-        this.templateCache = new HashMap<String, Template>(5);
-
-        this.artifacts = new ArrayList<Artifact>();
-    }
-
-    protected String defaultTemplateName(TemplateType type) {
-        switch (type) {
-            case ENTITY_SINGLE_CLASS:
-                return ClassGenerationAction.SINGLE_CLASS_TEMPLATE;
-            case ENTITY_SUBCLASS:
-                return ClassGenerationAction.SUBCLASS_TEMPLATE;
-            case ENTITY_SUPERCLASS:
-                return ClassGenerationAction.SUPERCLASS_TEMPLATE;
-            case EMBEDDABLE_SUBCLASS:
-                return ClassGenerationAction.EMBEDDABLE_SUBCLASS_TEMPLATE;
-            case EMBEDDABLE_SUPERCLASS:
-                return ClassGenerationAction.EMBEDDABLE_SUPERCLASS_TEMPLATE;
-            case EMBEDDABLE_SINGLE_CLASS:
-                return ClassGenerationAction.EMBEDDABLE_SINGLE_CLASS_TEMPLATE;
-            case DATAMAP_SINGLE_CLASS:
-                return ClassGenerationAction.DATAMAP_SINGLE_CLASS_TEMPLATE;
-            case DATAMAP_SUPERCLASS:
-                return ClassGenerationAction.DATAMAP_SUPERCLASS_TEMPLATE;
-            case DATAMAP_SUBCLASS:
-                return ClassGenerationAction.DATAMAP_SUBCLASS_TEMPLATE;
-            default:
-                throw new IllegalArgumentException("Invalid template type: " + 
type);
-        }
-    }
-
-    protected String customTemplateName(TemplateType type) {
-        switch (type) {
-            case ENTITY_SINGLE_CLASS:
-                return template;
-            case ENTITY_SUBCLASS:
-                return template;
-            case ENTITY_SUPERCLASS:
-                return superTemplate;
-            case EMBEDDABLE_SUBCLASS:
-                return embeddableTemplate;
-            case EMBEDDABLE_SUPERCLASS:
-                return embeddableSuperTemplate;
-            case DATAMAP_SINGLE_CLASS:
-                return queryTemplate;
-            case DATAMAP_SUPERCLASS:
-                return querySuperTemplate;
-            case DATAMAP_SUBCLASS:
-                return queryTemplate;
-            default:
-                throw new IllegalArgumentException("Invalid template type: " + 
type);
-        }
-    }
-
-    /**
-     * Returns a String used to prefix class name to create a generated 
superclass.
-     * Default value is "_".
-     */
-    protected String getSuperclassPrefix() {
-        return ClassGenerationAction.SUPERCLASS_PREFIX;
-    }
-
-    /**
-     * VelocityContext initialization method called once per artifact.
-     */
-    protected void resetContextForArtifact(Artifact artifact) {
-        StringUtils stringUtils = StringUtils.getInstance();
-
-        String qualifiedClassName = artifact.getQualifiedClassName();
-        String packageName = stringUtils.stripClass(qualifiedClassName);
-        String className = stringUtils.stripPackageName(qualifiedClassName);
-
-        String qualifiedBaseClassName = artifact.getQualifiedBaseClassName();
-        String basePackageName = 
stringUtils.stripClass(qualifiedBaseClassName);
-        String baseClassName = 
stringUtils.stripPackageName(qualifiedBaseClassName);
-
-        String superClassName = getSuperclassPrefix()
-                + stringUtils.stripPackageName(qualifiedClassName);
-
-        String superPackageName = this.superPkg;
-        if (superPackageName == null) {
-            superPackageName = packageName + ".auto";
-        }
-
-        context.put(Artifact.BASE_CLASS_KEY, baseClassName);
-        context.put(Artifact.BASE_PACKAGE_KEY, basePackageName);
-
-        context.put(Artifact.SUB_CLASS_KEY, className);
-        context.put(Artifact.SUB_PACKAGE_KEY, packageName);
-
-        context.put(Artifact.SUPER_CLASS_KEY, superClassName);
-        context.put(Artifact.SUPER_PACKAGE_KEY, superPackageName);
-
-        context.put(Artifact.OBJECT_KEY, artifact.getObject());
-        context.put(Artifact.STRING_UTILS_KEY, stringUtils);
-
-        context.put(Artifact.CREATE_PROPERTY_NAMES, createPropertyNames);
-    }
-
-    /**
-     * VelocityContext initialization method called once per each artifact and 
template
-     * type combination.
-     */
-    protected void resetContextForArtifactTemplate(
-            Artifact artifact,
-            TemplateType templateType) {
-        context.put(Artifact.IMPORT_UTILS_KEY, new ImportUtils());
-        artifact.postInitContext(context);
-    }
-
-    /**
-     * Executes class generation once per each artifact.
-     */
-    public void execute() throws Exception {
-
-        validateAttributes();
-
-        try {
-            for (Artifact artifact : artifacts) {
-                execute(artifact);
-            }
-        }
-        finally {
-            // must reset engine at the end of class generator run to avoid 
memory
-            // leaks and stale templates
-            this.templateCache.clear();
-        }
-    }
-
-    /**
-     * Executes class generation for a single artifact.
-     */
-    protected void execute(Artifact artifact) throws Exception {
-
-        resetContextForArtifact(artifact);
-
-        ArtifactGenerationMode artifactMode = makePairs
-                ? ArtifactGenerationMode.GENERATION_GAP
-                : ArtifactGenerationMode.SINGLE_CLASS;
-
-        TemplateType[] templateTypes = artifact.getTemplateTypes(artifactMode);
-        for (TemplateType type : templateTypes) {
-
-            Writer out = openWriter(type);
-            if (out != null) {
-
-                resetContextForArtifactTemplate(artifact, type);
-                getTemplate(type).merge(context, out);
-                out.close();
-            }
-        }
-    }
-
-    protected Template getTemplate(TemplateType type) throws Exception {
-
-        String templateName = customTemplateName(type);
-        if (templateName == null) {
-            templateName = defaultTemplateName(type);
-        }
-
-        // Velocity < 1.5 has some memory problems, so we will create a 
VelocityEngine
-        // every time, and store templates in an internal cache, to avoid 
uncontrolled
-        // memory leaks... Presumably 1.5 fixes it.
-
-        Template template = templateCache.get(templateName);
-
-        if (template == null) {
-
-            Properties props = new Properties();
-
-            // null logger that will prevent velocity.log from being generated
-            props.put(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, 
NullLogSystem.class
-                    .getName());
-            props.put("resource.loader", "cayenne");
-            props.put("cayenne.resource.loader.class", 
ClassGeneratorResourceLoader.class
-                    .getName());
-            props.put("cayenne.resource.loader.cache", "false");
-
-            VelocityEngine velocityEngine = new VelocityEngine();
-            velocityEngine.init(props);
-
-            template = velocityEngine.getTemplate(templateName);
-            templateCache.put(templateName, template);
-        }
-
-        return template;
-    }
-
-    /**
-     * Validates the state of this class generator. Throws 
CayenneRuntimeException if it
-     * is in an inconsistent state. Called internally from "execute".
-     */
-    protected void validateAttributes() {
-        if (destDir == null) {
-            throw new CayenneRuntimeException("'destDir' attribute is 
missing.");
-        }
-
-        if (!destDir.isDirectory()) {
-            throw new CayenneRuntimeException("'destDir' is not a directory.");
-        }
-
-        if (!destDir.canWrite()) {
-            throw new CayenneRuntimeException("Do not have write permissions 
for "
-                    + destDir);
-        }
-    }
-
-    /**
-     * Sets the destDir.
-     */
-    public void setDestDir(File destDir) {
-        this.destDir = destDir;
-    }
-
-    /**
-     * Sets <code>overwrite</code> property.
-     */
-    public void setOverwrite(boolean overwrite) {
-        this.overwrite = overwrite;
-    }
-
-    /**
-     * Sets <code>makepairs</code> property.
-     */
-    public void setMakePairs(boolean makePairs) {
-        this.makePairs = makePairs;
-    }
-
-    /**
-     * Sets <code>template</code> property.
-     */
-    public void setTemplate(String template) {
-        this.template = template;
-    }
-
-    /**
-     * Sets <code>superTemplate</code> property.
-     */
-    public void setSuperTemplate(String superTemplate) {
-        this.superTemplate = superTemplate;
-    }
-
-    public void setQueryTemplate(String queryTemplate) {
-        this.queryTemplate = queryTemplate;
-    }
-
-    public void setQuerySuperTemplate(String querySuperTemplate) {
-        this.querySuperTemplate = querySuperTemplate;
-    }
-
-    /**
-     * Sets <code>usepkgpath</code> property.
-     */
-    public void setUsePkgPath(boolean usePkgPath) {
-        this.usePkgPath = usePkgPath;
-    }
-
-    /**
-     * Sets <code>outputPattern</code> property.
-     */
-    public void setOutputPattern(String outputPattern) {
-        this.outputPattern = outputPattern;
-    }
-
-    /**
-     * Sets <code>createPropertyNames</code> property.
-     */
-    public void setCreatePropertyNames(boolean createPropertyNames) {
-        this.createPropertyNames = createPropertyNames;
-    }
-
-    /**
-     * Opens a Writer to write generated output. Returned Writer is mapped to 
a filesystem
-     * file (although subclasses may override that). File location is 
determined from the
-     * current state of VelocityContext and the TemplateType passed as a 
parameter. Writer
-     * encoding is determined from the value of the "encoding" property.
-     */
-    protected Writer openWriter(TemplateType templateType) throws Exception {
-
-        File outFile = (templateType.isSuperclass())
-                ? fileForSuperclass()
-                : fileForClass();
-        if (outFile == null) {
-            return null;
-        }
-
-        if (logger != null) {
-            String label = templateType.isSuperclass() ? "superclass" : 
"class";
-            logger.info("Generating " + label + " file: " + 
outFile.getCanonicalPath());
-        }
-
-        // return writer with specified encoding
-        FileOutputStream out = new FileOutputStream(outFile);
-
-        return (encoding != null)
-                ? new OutputStreamWriter(out, encoding)
-                : new OutputStreamWriter(out);
-    }
-
-    /**
-     * Returns a target file where a generated superclass must be saved. If 
null is
-     * returned, class shouldn't be generated.
-     */
-    protected File fileForSuperclass() throws Exception {
-
-        String packageName = (String) context.get(Artifact.SUPER_PACKAGE_KEY);
-        String className = (String) context.get(Artifact.SUPER_CLASS_KEY);
-
-        String filename = NamePatternMatcher.replaceWildcardInStringWithString(
-                WILDCARD,
-                outputPattern,
-                className);
-        File dest = new File(mkpath(destDir, packageName), filename);
-
-        // Ignore if the destination is newer than the map
-        // (internal timestamp), i.e. has been generated after the map was
-        // last saved AND the template is older than the destination file
-        if (dest.exists() && !isOld(dest)) {
-
-            if (superTemplate == null) {
-                return null;
-            }
-
-            File superTemplateFile = new File(superTemplate);
-            if (superTemplateFile.lastModified() < dest.lastModified()) {
-                return null;
-            }
-        }
-
-        return dest;
-    }
-
-    /**
-     * Returns a target file where a generated class must be saved. If null is 
returned,
-     * class shouldn't be generated.
-     */
-    protected File fileForClass() throws Exception {
-
-        String packageName = (String) context.get(Artifact.SUB_PACKAGE_KEY);
-        String className = (String) context.get(Artifact.SUB_CLASS_KEY);
-
-        String filename = NamePatternMatcher.replaceWildcardInStringWithString(
-                WILDCARD,
-                outputPattern,
-                className);
-        File dest = new File(mkpath(destDir, packageName), filename);
-
-        if (dest.exists()) {
-            // no overwrite of subclasses
-            if (makePairs) {
-                return null;
-            }
-
-            // skip if said so
-            if (!overwrite) {
-                return null;
-            }
-
-            // Ignore if the destination is newer than the map
-            // (internal timestamp), i.e. has been generated after the map was
-            // last saved AND the template is older than the destination file
-            if (!isOld(dest)) {
-
-                if (template == null) {
-                    return null;
-                }
-
-                File templateFile = new File(template);
-                if (templateFile.lastModified() < dest.lastModified()) {
-                    return null;
-                }
-            }
-        }
-
-        return dest;
-    }
-
-    /**
-     * Returns true if <code>file</code> parameter is older than internal 
timestamp of
-     * this class generator.
-     */
-    protected boolean isOld(File file) {
-        return file.lastModified() <= timestamp;
-    }
-
-    /**
-     * Returns a File object corresponding to a directory where files that 
belong to
-     * <code>pkgName</code> package should reside. Creates any missing 
diectories below
-     * <code>dest</code>.
-     */
-    protected File mkpath(File dest, String pkgName) throws Exception {
-
-        if (!usePkgPath || pkgName == null) {
-            return dest;
-        }
-
-        String path = pkgName.replace('.', File.separatorChar);
-        File fullPath = new File(dest, path);
-        if (!fullPath.isDirectory() && !fullPath.mkdirs()) {
-            throw new Exception("Error making path: " + fullPath);
-        }
-
-        return fullPath;
-    }
-
-    public void setTimestamp(long timestamp) {
-        this.timestamp = timestamp;
-    }
-
-    /**
-     * Sets file encoding. If set to null, default system encoding will be 
used.
-     */
-    public void setEncoding(String encoding) {
-        this.encoding = encoding;
-    }
-
-    /**
-     * Sets "superPkg" property value.
-     */
-    public void setSuperPkg(String superPkg) {
-        this.superPkg = superPkg;
-    }
-
-    /**
-     * @param dataMap The dataMap to set.
-     */
-    public void setDataMap(DataMap dataMap) {
-        this.dataMap = dataMap;
-    }
-
-    /**
-     * Adds entities to the internal entity list.
-     */
-    public void addEntities(Collection<ObjEntity> entities) {
-        if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY || 
-                artifactsGenerationMode == ArtifactsGenerationMode.ALL ) {
-            if (entities != null) {
-                for (ObjEntity entity : entities) {
-                    artifacts.add(new EntityArtifact(entity));
-                }
-            }
-        }
-    }
-
-    public void addEmbeddables(Collection<Embeddable> embeddables) {
-        if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY || 
-                artifactsGenerationMode == ArtifactsGenerationMode.ALL ) {
-            if (embeddables != null) {
-                for (Embeddable embeddable : embeddables) {
-                    artifacts.add(new EmbeddableArtifact(embeddable));
-                }
-            }
-        }
-    }
-
-    public void addQueries(Collection<Query> queries) {
-        if (artifactsGenerationMode == ArtifactsGenerationMode.DATAMAP
-                || artifactsGenerationMode == ArtifactsGenerationMode.ALL) {
-
-            // TODO: andrus 10.12.2010 - why not also check for empty query 
list?? Or
-            // create a better API for enabling DataMapArtifact
-            if (queries != null) {
-                artifacts.add(new DataMapArtifact(dataMap, queries));
-            }
-        }
-    }
-
-    /**
-     * Sets an optional shared VelocityContext. Useful with tools like VPP 
that can set
-     * custom values in the context, not known to Cayenne.
-     */
-    public void setContext(VelocityContext context) {
-        this.context = context;
-    }
-
-    /**
-     * Injects an optional logger that will be used to trace generated files 
at the info
-     * level.
-     */
-    public void setLogger(Log logger) {
-        this.logger = logger;
-    }
-
-    public void setEmbeddableTemplate(String embeddableTemplate) {
-        this.embeddableTemplate = embeddableTemplate;
-    }
-
-    public void setEmbeddableSuperTemplate(String embeddableSuperTemplate) {
-        this.embeddableSuperTemplate = embeddableSuperTemplate;
-    }
-
-    public void setArtifactsGenerationMode(String mode) {
-        if (ArtifactsGenerationMode.ENTITY.getLabel().equalsIgnoreCase(mode)) {
-            this.artifactsGenerationMode = ArtifactsGenerationMode.ENTITY;
-        }
-        else if 
(ArtifactsGenerationMode.DATAMAP.getLabel().equalsIgnoreCase(mode)) {
-            this.artifactsGenerationMode = ArtifactsGenerationMode.DATAMAP;
-        }
-        else {
-            this.artifactsGenerationMode = ArtifactsGenerationMode.ALL;
-        }
-    }
+       static final String TEMPLATES_DIR_NAME = "templates/v1_2/";
+
+       public static final String SINGLE_CLASS_TEMPLATE = TEMPLATES_DIR_NAME + 
"singleclass.vm";
+       public static final String SUBCLASS_TEMPLATE = TEMPLATES_DIR_NAME + 
"subclass.vm";
+       public static final String SUPERCLASS_TEMPLATE = TEMPLATES_DIR_NAME + 
"superclass.vm";
+
+       public static final String EMBEDDABLE_SINGLE_CLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "embeddable-singleclass.vm";
+       public static final String EMBEDDABLE_SUBCLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "embeddable-subclass.vm";
+       public static final String EMBEDDABLE_SUPERCLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "embeddable-superclass.vm";
+
+       public static final String DATAMAP_SINGLE_CLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "datamap-singleclass.vm";
+       public static final String DATAMAP_SUBCLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "datamap-subclass.vm";
+       public static final String DATAMAP_SUPERCLASS_TEMPLATE = 
TEMPLATES_DIR_NAME + "datamap-superclass.vm";
+
+       public static final String SUPERCLASS_PREFIX = "_";
+       private static final String WILDCARD = "*";
+
+       protected Collection<Artifact> artifacts;
+
+       protected String superPkg;
+       protected DataMap dataMap;
+
+       protected ArtifactsGenerationMode artifactsGenerationMode;
+       protected boolean makePairs;
+
+       protected Log logger;
+       protected File destDir;
+       protected boolean overwrite;
+       protected boolean usePkgPath;
+
+       protected String template;
+       protected String superTemplate;
+       protected String embeddableTemplate;
+       protected String embeddableSuperTemplate;
+       protected String queryTemplate;
+       protected String querySuperTemplate;
+       protected long timestamp;
+       protected String outputPattern;
+       protected String encoding;
+       protected boolean createPropertyNames;
+
+       // runtime ivars
+       protected VelocityContext context;
+       protected Map<String, Template> templateCache;
+
+       public ClassGenerationAction() {
+               this.outputPattern = "*.java";
+               this.timestamp = System.currentTimeMillis();
+               this.usePkgPath = true;
+               this.makePairs = true;
+               this.context = new VelocityContext();
+               this.templateCache = new HashMap<String, Template>(5);
+
+               this.artifacts = new ArrayList<Artifact>();
+       }
+
+       protected String defaultTemplateName(TemplateType type) {
+               switch (type) {
+               case ENTITY_SINGLE_CLASS:
+                       return ClassGenerationAction.SINGLE_CLASS_TEMPLATE;
+               case ENTITY_SUBCLASS:
+                       return ClassGenerationAction.SUBCLASS_TEMPLATE;
+               case ENTITY_SUPERCLASS:
+                       return ClassGenerationAction.SUPERCLASS_TEMPLATE;
+               case EMBEDDABLE_SUBCLASS:
+                       return 
ClassGenerationAction.EMBEDDABLE_SUBCLASS_TEMPLATE;
+               case EMBEDDABLE_SUPERCLASS:
+                       return 
ClassGenerationAction.EMBEDDABLE_SUPERCLASS_TEMPLATE;
+               case EMBEDDABLE_SINGLE_CLASS:
+                       return 
ClassGenerationAction.EMBEDDABLE_SINGLE_CLASS_TEMPLATE;
+               case DATAMAP_SINGLE_CLASS:
+                       return 
ClassGenerationAction.DATAMAP_SINGLE_CLASS_TEMPLATE;
+               case DATAMAP_SUPERCLASS:
+                       return 
ClassGenerationAction.DATAMAP_SUPERCLASS_TEMPLATE;
+               case DATAMAP_SUBCLASS:
+                       return ClassGenerationAction.DATAMAP_SUBCLASS_TEMPLATE;
+               default:
+                       throw new IllegalArgumentException("Invalid template 
type: " + type);
+               }
+       }
+
+       protected String customTemplateName(TemplateType type) {
+               switch (type) {
+               case ENTITY_SINGLE_CLASS:
+                       return template;
+               case ENTITY_SUBCLASS:
+                       return template;
+               case ENTITY_SUPERCLASS:
+                       return superTemplate;
+               case EMBEDDABLE_SUBCLASS:
+                       return embeddableTemplate;
+               case EMBEDDABLE_SUPERCLASS:
+                       return embeddableSuperTemplate;
+               case DATAMAP_SINGLE_CLASS:
+                       return queryTemplate;
+               case DATAMAP_SUPERCLASS:
+                       return querySuperTemplate;
+               case DATAMAP_SUBCLASS:
+                       return queryTemplate;
+               default:
+                       throw new IllegalArgumentException("Invalid template 
type: " + type);
+               }
+       }
+
+       /**
+        * Returns a String used to prefix class name to create a generated
+        * superclass. Default value is "_".
+        */
+       protected String getSuperclassPrefix() {
+               return ClassGenerationAction.SUPERCLASS_PREFIX;
+       }
+
+       /**
+        * VelocityContext initialization method called once per artifact.
+        */
+       protected void resetContextForArtifact(Artifact artifact) {
+               StringUtils stringUtils = StringUtils.getInstance();
+
+               String qualifiedClassName = artifact.getQualifiedClassName();
+               String packageName = stringUtils.stripClass(qualifiedClassName);
+               String className = 
stringUtils.stripPackageName(qualifiedClassName);
+
+               String qualifiedBaseClassName = 
artifact.getQualifiedBaseClassName();
+               String basePackageName = 
stringUtils.stripClass(qualifiedBaseClassName);
+               String baseClassName = 
stringUtils.stripPackageName(qualifiedBaseClassName);
+
+               String superClassName = getSuperclassPrefix() + 
stringUtils.stripPackageName(qualifiedClassName);
+
+               String superPackageName = this.superPkg;
+               if (superPackageName == null) {
+                       superPackageName = packageName + ".auto";
+               }
+
+               context.put(Artifact.BASE_CLASS_KEY, baseClassName);
+               context.put(Artifact.BASE_PACKAGE_KEY, basePackageName);
+
+               context.put(Artifact.SUB_CLASS_KEY, className);
+               context.put(Artifact.SUB_PACKAGE_KEY, packageName);
+
+               context.put(Artifact.SUPER_CLASS_KEY, superClassName);
+               context.put(Artifact.SUPER_PACKAGE_KEY, superPackageName);
+
+               context.put(Artifact.OBJECT_KEY, artifact.getObject());
+               context.put(Artifact.STRING_UTILS_KEY, stringUtils);
+
+               context.put(Artifact.CREATE_PROPERTY_NAMES, 
createPropertyNames);
+       }
+
+       /**
+        * VelocityContext initialization method called once per each artifact 
and
+        * template type combination.
+        */
+       protected void resetContextForArtifactTemplate(Artifact artifact, 
TemplateType templateType) {
+               context.put(Artifact.IMPORT_UTILS_KEY, new ImportUtils());
+               artifact.postInitContext(context);
+       }
+
+       /**
+        * Executes class generation once per each artifact.
+        */
+       public void execute() throws Exception {
+
+               validateAttributes();
+
+               try {
+                       for (Artifact artifact : artifacts) {
+                               execute(artifact);
+                       }
+               } finally {
+                       // must reset engine at the end of class generator run 
to avoid
+                       // memory
+                       // leaks and stale templates
+                       this.templateCache.clear();
+               }
+       }
+
+       /**
+        * Executes class generation for a single artifact.
+        */
+       protected void execute(Artifact artifact) throws Exception {
+
+               resetContextForArtifact(artifact);
+
+               ArtifactGenerationMode artifactMode = makePairs ? 
ArtifactGenerationMode.GENERATION_GAP
+                               : ArtifactGenerationMode.SINGLE_CLASS;
+
+               TemplateType[] templateTypes = 
artifact.getTemplateTypes(artifactMode);
+               for (TemplateType type : templateTypes) {
+
+                       try (Writer out = openWriter(type);) {
+                               if (out != null) {
+
+                                       
resetContextForArtifactTemplate(artifact, type);
+                                       getTemplate(type).merge(context, out);
+                               }
+                       }
+               }
+       }
+
+       protected Template getTemplate(TemplateType type) throws Exception {
+
+               String templateName = customTemplateName(type);
+               if (templateName == null) {
+                       templateName = defaultTemplateName(type);
+               }
+
+               // Velocity < 1.5 has some memory problems, so we will create a
+               // VelocityEngine
+               // every time, and store templates in an internal cache, to 
avoid
+               // uncontrolled
+               // memory leaks... Presumably 1.5 fixes it.
+
+               Template template = templateCache.get(templateName);
+
+               if (template == null) {
+
+                       Properties props = new Properties();
+
+                       // null logger that will prevent velocity.log from 
being generated
+                       props.put(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, 
NullLogSystem.class.getName());
+                       props.put("resource.loader", "cayenne");
+                       props.put("cayenne.resource.loader.class", 
ClassGeneratorResourceLoader.class.getName());
+                       props.put("cayenne.resource.loader.cache", "false");
+
+                       VelocityEngine velocityEngine = new VelocityEngine();
+                       velocityEngine.init(props);
+
+                       template = velocityEngine.getTemplate(templateName);
+                       templateCache.put(templateName, template);
+               }
+
+               return template;
+       }
+
+       /**
+        * Validates the state of this class generator. Throws
+        * CayenneRuntimeException if it is in an inconsistent state. Called
+        * internally from "execute".
+        */
+       protected void validateAttributes() {
+               if (destDir == null) {
+                       throw new CayenneRuntimeException("'destDir' attribute 
is missing.");
+               }
+
+               if (!destDir.isDirectory()) {
+                       throw new CayenneRuntimeException("'destDir' is not a 
directory.");
+               }
+
+               if (!destDir.canWrite()) {
+                       throw new CayenneRuntimeException("Do not have write 
permissions for " + destDir);
+               }
+       }
+
+       /**
+        * Sets the destDir.
+        */
+       public void setDestDir(File destDir) {
+               this.destDir = destDir;
+       }
+
+       /**
+        * Sets <code>overwrite</code> property.
+        */
+       public void setOverwrite(boolean overwrite) {
+               this.overwrite = overwrite;
+       }
+
+       /**
+        * Sets <code>makepairs</code> property.
+        */
+       public void setMakePairs(boolean makePairs) {
+               this.makePairs = makePairs;
+       }
+
+       /**
+        * Sets <code>template</code> property.
+        */
+       public void setTemplate(String template) {
+               this.template = template;
+       }
+
+       /**
+        * Sets <code>superTemplate</code> property.
+        */
+       public void setSuperTemplate(String superTemplate) {
+               this.superTemplate = superTemplate;
+       }
+
+       public void setQueryTemplate(String queryTemplate) {
+               this.queryTemplate = queryTemplate;
+       }
+
+       public void setQuerySuperTemplate(String querySuperTemplate) {
+               this.querySuperTemplate = querySuperTemplate;
+       }
+
+       /**
+        * Sets <code>usepkgpath</code> property.
+        */
+       public void setUsePkgPath(boolean usePkgPath) {
+               this.usePkgPath = usePkgPath;
+       }
+
+       /**
+        * Sets <code>outputPattern</code> property.
+        */
+       public void setOutputPattern(String outputPattern) {
+               this.outputPattern = outputPattern;
+       }
+
+       /**
+        * Sets <code>createPropertyNames</code> property.
+        */
+       public void setCreatePropertyNames(boolean createPropertyNames) {
+               this.createPropertyNames = createPropertyNames;
+       }
+
+       /**
+        * Opens a Writer to write generated output. Returned Writer is mapped 
to a
+        * filesystem file (although subclasses may override that). File 
location is
+        * determined from the current state of VelocityContext and the 
TemplateType
+        * passed as a parameter. Writer encoding is determined from the value 
of
+        * the "encoding" property.
+        */
+       protected Writer openWriter(TemplateType templateType) throws Exception 
{
+
+               File outFile = (templateType.isSuperclass()) ? 
fileForSuperclass() : fileForClass();
+               if (outFile == null) {
+                       return null;
+               }
+
+               if (logger != null) {
+                       String label = templateType.isSuperclass() ? 
"superclass" : "class";
+                       logger.info("Generating " + label + " file: " + 
outFile.getCanonicalPath());
+               }
+
+               // return writer with specified encoding
+               FileOutputStream out = new FileOutputStream(outFile);
+
+               return (encoding != null) ? new OutputStreamWriter(out, 
encoding) : new OutputStreamWriter(out);
+       }
+
+       /**
+        * Returns a target file where a generated superclass must be saved. If 
null
+        * is returned, class shouldn't be generated.
+        */
+       protected File fileForSuperclass() throws Exception {
+
+               String packageName = (String) 
context.get(Artifact.SUPER_PACKAGE_KEY);
+               String className = (String) 
context.get(Artifact.SUPER_CLASS_KEY);
+
+               String filename = 
NamePatternMatcher.replaceWildcardInStringWithString(WILDCARD, outputPattern, 
className);
+               File dest = new File(mkpath(destDir, packageName), filename);
+
+               // Ignore if the destination is newer than the map
+               // (internal timestamp), i.e. has been generated after the map 
was
+               // last saved AND the template is older than the destination 
file
+               if (dest.exists() && !isOld(dest)) {
+
+                       if (superTemplate == null) {
+                               return null;
+                       }
+
+                       File superTemplateFile = new File(superTemplate);
+                       if (superTemplateFile.lastModified() < 
dest.lastModified()) {
+                               return null;
+                       }
+               }
+
+               return dest;
+       }
+
+       /**
+        * Returns a target file where a generated class must be saved. If null 
is
+        * returned, class shouldn't be generated.
+        */
+       protected File fileForClass() throws Exception {
+
+               String packageName = (String) 
context.get(Artifact.SUB_PACKAGE_KEY);
+               String className = (String) context.get(Artifact.SUB_CLASS_KEY);
+
+               String filename = 
NamePatternMatcher.replaceWildcardInStringWithString(WILDCARD, outputPattern, 
className);
+               File dest = new File(mkpath(destDir, packageName), filename);
+
+               if (dest.exists()) {
+                       // no overwrite of subclasses
+                       if (makePairs) {
+                               return null;
+                       }
+
+                       // skip if said so
+                       if (!overwrite) {
+                               return null;
+                       }
+
+                       // Ignore if the destination is newer than the map
+                       // (internal timestamp), i.e. has been generated after 
the map was
+                       // last saved AND the template is older than the 
destination file
+                       if (!isOld(dest)) {
+
+                               if (template == null) {
+                                       return null;
+                               }
+
+                               File templateFile = new File(template);
+                               if (templateFile.lastModified() < 
dest.lastModified()) {
+                                       return null;
+                               }
+                       }
+               }
+
+               return dest;
+       }
+
+       /**
+        * Returns true if <code>file</code> parameter is older than internal
+        * timestamp of this class generator.
+        */
+       protected boolean isOld(File file) {
+               return file.lastModified() <= timestamp;
+       }
+
+       /**
+        * Returns a File object corresponding to a directory where files that
+        * belong to <code>pkgName</code> package should reside. Creates any 
missing
+        * diectories below <code>dest</code>.
+        */
+       protected File mkpath(File dest, String pkgName) throws Exception {
+
+               if (!usePkgPath || pkgName == null) {
+                       return dest;
+               }
+
+               String path = pkgName.replace('.', File.separatorChar);
+               File fullPath = new File(dest, path);
+               if (!fullPath.isDirectory() && !fullPath.mkdirs()) {
+                       throw new Exception("Error making path: " + fullPath);
+               }
+
+               return fullPath;
+       }
+
+       public void setTimestamp(long timestamp) {
+               this.timestamp = timestamp;
+       }
+
+       /**
+        * Sets file encoding. If set to null, default system encoding will be 
used.
+        */
+       public void setEncoding(String encoding) {
+               this.encoding = encoding;
+       }
+
+       /**
+        * Sets "superPkg" property value.
+        */
+       public void setSuperPkg(String superPkg) {
+               this.superPkg = superPkg;
+       }
+
+       /**
+        * @param dataMap
+        *            The dataMap to set.
+        */
+       public void setDataMap(DataMap dataMap) {
+               this.dataMap = dataMap;
+       }
+
+       /**
+        * Adds entities to the internal entity list.
+        */
+       public void addEntities(Collection<ObjEntity> entities) {
+               if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY
+                               || artifactsGenerationMode == 
ArtifactsGenerationMode.ALL) {
+                       if (entities != null) {
+                               for (ObjEntity entity : entities) {
+                                       artifacts.add(new 
EntityArtifact(entity));
+                               }
+                       }
+               }
+       }
+
+       public void addEmbeddables(Collection<Embeddable> embeddables) {
+               if (artifactsGenerationMode == ArtifactsGenerationMode.ENTITY
+                               || artifactsGenerationMode == 
ArtifactsGenerationMode.ALL) {
+                       if (embeddables != null) {
+                               for (Embeddable embeddable : embeddables) {
+                                       artifacts.add(new 
EmbeddableArtifact(embeddable));
+                               }
+                       }
+               }
+       }
+
+       public void addQueries(Collection<Query> queries) {
+               if (artifactsGenerationMode == ArtifactsGenerationMode.DATAMAP
+                               || artifactsGenerationMode == 
ArtifactsGenerationMode.ALL) {
+
+                       // TODO: andrus 10.12.2010 - why not also check for 
empty query
+                       // list?? Or
+                       // create a better API for enabling DataMapArtifact
+                       if (queries != null) {
+                               artifacts.add(new DataMapArtifact(dataMap, 
queries));
+                       }
+               }
+       }
+
+       /**
+        * Sets an optional shared VelocityContext. Useful with tools like VPP 
that
+        * can set custom values in the context, not known to Cayenne.
+        */
+       public void setContext(VelocityContext context) {
+               this.context = context;
+       }
+
+       /**
+        * Injects an optional logger that will be used to trace generated 
files at
+        * the info level.
+        */
+       public void setLogger(Log logger) {
+               this.logger = logger;
+       }
+
+       public void setEmbeddableTemplate(String embeddableTemplate) {
+               this.embeddableTemplate = embeddableTemplate;
+       }
+
+       public void setEmbeddableSuperTemplate(String embeddableSuperTemplate) {
+               this.embeddableSuperTemplate = embeddableSuperTemplate;
+       }
+
+       public void setArtifactsGenerationMode(String mode) {
+               if 
(ArtifactsGenerationMode.ENTITY.getLabel().equalsIgnoreCase(mode)) {
+                       this.artifactsGenerationMode = 
ArtifactsGenerationMode.ENTITY;
+               } else if 
(ArtifactsGenerationMode.DATAMAP.getLabel().equalsIgnoreCase(mode)) {
+                       this.artifactsGenerationMode = 
ArtifactsGenerationMode.DATAMAP;
+               } else {
+                       this.artifactsGenerationMode = 
ArtifactsGenerationMode.ALL;
+               }
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportAction.java
----------------------------------------------------------------------
diff --git 
a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportAction.java
 
b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportAction.java
index d604c45..2683d97 100644
--- 
a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportAction.java
+++ 
b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportAction.java
@@ -18,6 +18,20 @@
  */
 package org.apache.cayenne.tools.dbimport;
 
+import static org.apache.commons.lang.StringUtils.isBlank;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.sql.Connection;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.sql.DataSource;
+
 import org.apache.cayenne.access.DbLoader;
 import org.apache.cayenne.configuration.ConfigurationTree;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
@@ -25,8 +39,20 @@ import 
org.apache.cayenne.configuration.server.DataSourceFactory;
 import org.apache.cayenne.configuration.server.DbAdapterFactory;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.map.*;
-import org.apache.cayenne.merge.*;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.MapLoader;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.merge.AbstractToModelToken;
+import org.apache.cayenne.merge.AddRelationshipToDb;
+import org.apache.cayenne.merge.DbMerger;
+import org.apache.cayenne.merge.ExecutingMergerContext;
+import org.apache.cayenne.merge.MergerContext;
+import org.apache.cayenne.merge.MergerFactory;
+import org.apache.cayenne.merge.MergerToken;
+import org.apache.cayenne.merge.ModelMergeDelegate;
+import org.apache.cayenne.merge.ProxyModelMergeDelegate;
 import org.apache.cayenne.project.Project;
 import org.apache.cayenne.project.ProjectSaver;
 import org.apache.cayenne.resource.URLResource;
@@ -36,15 +62,6 @@ import org.apache.cayenne.validation.ValidationResult;
 import org.apache.commons.logging.Log;
 import org.xml.sax.InputSource;
 
-import javax.sql.DataSource;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.sql.Connection;
-import java.util.*;
-
-import static org.apache.commons.lang.StringUtils.isBlank;
-
 /**
  * A thin wrapper around {@link DbLoader} that encapsulates DB import logic for
  * the benefit of Ant and Maven db importers.
@@ -53,214 +70,205 @@ import static org.apache.commons.lang.StringUtils.isBlank;
  */
 public class DbImportAction {
 
-    private final ProjectSaver projectSaver;
-    private final Log logger;
-    private final DataSourceFactory dataSourceFactory;
-    private final DbAdapterFactory adapterFactory;
-    private final MapLoader mapLoader;
-
-    public DbImportAction(@Inject Log logger,
-                          @Inject ProjectSaver projectSaver,
-                          @Inject DataSourceFactory dataSourceFactory,
-                          @Inject DbAdapterFactory adapterFactory,
-                          @Inject MapLoader mapLoader) {
-        this.logger = logger;
-        this.projectSaver = projectSaver;
-        this.dataSourceFactory = dataSourceFactory;
-        this.adapterFactory = adapterFactory;
-        this.mapLoader = mapLoader;
-    }
-
-    public void execute(DbImportConfiguration config) throws Exception {
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("DB connection: " + config.getDataSourceInfo());
-        }
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(config);
-        }
-
-        DataNodeDescriptor dataNodeDescriptor = 
config.createDataNodeDescriptor();
-        DataSource dataSource = 
dataSourceFactory.getDataSource(dataNodeDescriptor);
-        DbAdapter adapter = adapterFactory.createAdapter(dataNodeDescriptor, 
dataSource);
-
-        DataMap loadedFomDb = load(config, adapter, 
dataSource.getConnection());
-        if (loadedFomDb == null) {
-            logger.info("Nothing was loaded from db.");
-            return;
-        }
-
-        DataMap existing = loadExistingDataMap(config.getDataMapFile());
-        if (existing == null) {
-            logger.info("");
-            File file = config.getDataMapFile();
-            logger.info("Map file does not exist. Loaded db model will be 
saved into '"
-                    + (file == null ? "null" : file.getAbsolutePath() + "'"));
-
-            saveLoaded(config.initializeDataMap(loadedFomDb));
-        } else {
-            MergerFactory mergerFactory = adapter.mergerFactory();
-
-            List<MergerToken> mergeTokens = new DbMerger(mergerFactory)
-                    .createMergeTokens(existing, loadedFomDb, 
config.getDbLoaderConfig());
-            if (mergeTokens.isEmpty()) {
-                logger.info("");
-                logger.info("Detected changes: No changes to import.");
-                return;
-            }
-
-            if (!isBlank(config.getDefaultPackage())) {
-                existing.setDefaultPackage(config.getDefaultPackage());
-            }
-
-            final Collection<ObjEntity> loadedObjEntities = new 
LinkedList<ObjEntity>();
-            DataMap executed = execute(new 
ProxyModelMergeDelegate(config.createMergeDelegate()) {
-                @Override
-                public void objEntityAdded(ObjEntity ent) {
-                    loadedObjEntities.add(ent);
-
-                    super.objEntityAdded(ent);
-                }
-
-            }, existing, log(sort(reverse(mergerFactory, mergeTokens))));
-
-            DbLoader.flattenManyToManyRelationships(executed, 
loadedObjEntities, config.getNameGenerator());
-
-            relationshipsSanity(executed);
-
-
-            saveLoaded(executed);
-        }
-    }
-
-    private void relationshipsSanity(DataMap executed) {
-        for (ObjEntity objEntity : executed.getObjEntities()) {
-
-            List<ObjRelationship> rels = new 
LinkedList<ObjRelationship>(objEntity.getRelationships());
-            for (ObjRelationship rel : rels) {
-                if (rel.getSourceEntity() == null || rel.getTargetEntity() == 
null) {
-                    logger.error("Incorrect obj relationship source or target 
entity is null: " + rel);
-
-                    objEntity.removeRelationship(rel.getName());
-                }
-            }
-        }
-    }
-
-    protected static List<MergerToken> sort(List<MergerToken> reverse) {
-        Collections.sort(reverse, new Comparator<MergerToken>() {
-            @Override
-            public int compare(MergerToken o1, MergerToken o2) {
-                if (o1 instanceof AddRelationshipToDb && o2 instanceof 
AddRelationshipToDb) {
-                    return 0;
-                }
-
-                if (!(o1 instanceof AddRelationshipToDb || o2 instanceof 
AddRelationshipToDb)) {
-                    return 
o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
-                }
-
-                return o1 instanceof AddRelationshipToDb ? 1 : -1;
-            }
-        });
-
-        return reverse;
-    }
-
-    private Collection<MergerToken> log(List<MergerToken> tokens) {
-        logger.info("");
-        if (tokens.isEmpty()) {
-            logger.info("Detected changes: No changes to import.");
-            return tokens;
-        }
-
-        logger.info("Detected changes: ");
-        for (MergerToken token : tokens) {
-            logger.info(String.format("    %-20s %s", token.getTokenName(), 
token.getTokenValue()));
-        }
-        logger.info("");
-
-        return tokens;
-    }
-
-    private DataMap loadExistingDataMap(File dataMapFile) throws IOException {
-        if (dataMapFile != null && dataMapFile.exists() && 
dataMapFile.canRead()) {
-            DataMap dataMap = mapLoader.loadDataMap(new 
InputSource(dataMapFile.getCanonicalPath()));
-            dataMap.setNamespace(new 
EntityResolver(Collections.singleton(dataMap)));
-            dataMap.setConfigurationSource(new 
URLResource(dataMapFile.toURI().toURL()));
-
-            return dataMap;
-        }
-
-        return null;
-    }
-
-    private List<MergerToken> reverse(MergerFactory mergerFactory, 
Iterable<MergerToken> mergeTokens) throws IOException {
-        List<MergerToken> tokens = new LinkedList<MergerToken>();
-        for (MergerToken token : mergeTokens) {
-            if (token instanceof AbstractToModelToken) {
-                continue;
-            }
-            tokens.add(token.createReverse(mergerFactory));
-        }
-        return tokens;
-    }
-
-    /**
-     * Performs configured schema operations via DbGenerator.
-     */
-    private DataMap execute(ModelMergeDelegate mergeDelegate, DataMap dataMap, 
Collection<MergerToken> tokens) {
-        MergerContext mergerContext = new ExecutingMergerContext(
-                dataMap, null, null, mergeDelegate);
-
-        for (MergerToken tok : tokens) {
-            try {
-                tok.execute(mergerContext);
-            } catch (Throwable th) {
-                String message = "Migration Error. Can't apply changes from 
token: " + tok.getTokenName()
-                        + " (" + tok.getTokenValue() + ")";
-
-                logger.error(message, th);
-                mergerContext.getValidationResult().addFailure(new 
SimpleValidationFailure(th, message));
-            }
-        }
-
-        ValidationResult failures = mergerContext.getValidationResult();
-        if (failures == null || !failures.hasFailures()) {
-            logger.info("Migration Complete Successfully.");
-        } else {
-            logger.info("Migration Complete.");
-            logger.warn("Migration finished. The following problem(s) were 
ignored.");
-            for (ValidationFailure failure : failures.getFailures()) {
-                logger.warn(failure.toString());
-            }
-        }
-
-        return dataMap;
-    }
-
-    private DbLoader getLoader(DbImportConfiguration config, DbAdapter 
adapter, Connection connection) throws InstantiationException, 
IllegalAccessException, ClassNotFoundException {
-        return config.createLoader(adapter, connection, 
config.createLoaderDelegate());
-    }
-
-    void saveLoaded(DataMap dataMap) throws FileNotFoundException {
-        ConfigurationTree<DataMap> projectRoot = new 
ConfigurationTree<DataMap>(dataMap);
-        Project project = new Project(projectRoot);
-        projectSaver.save(project);
-    }
-
-    private DataMap load(DbImportConfiguration config, DbAdapter adapter, 
Connection connection) throws Exception {
-        DataMap dataMap = config.createDataMap();
-
-        try {
-            DbLoader loader = config.createLoader(adapter, connection, 
config.createLoaderDelegate());
-            loader.load(dataMap, config.getDbLoaderConfig());
-        } finally {
-            if (connection != null) {
-                connection.close();
-            }
-        }
-
-        return dataMap;
-    }
+       private final ProjectSaver projectSaver;
+       private final Log logger;
+       private final DataSourceFactory dataSourceFactory;
+       private final DbAdapterFactory adapterFactory;
+       private final MapLoader mapLoader;
+
+       public DbImportAction(@Inject Log logger, @Inject ProjectSaver 
projectSaver,
+                       @Inject DataSourceFactory dataSourceFactory, @Inject 
DbAdapterFactory adapterFactory,
+                       @Inject MapLoader mapLoader) {
+               this.logger = logger;
+               this.projectSaver = projectSaver;
+               this.dataSourceFactory = dataSourceFactory;
+               this.adapterFactory = adapterFactory;
+               this.mapLoader = mapLoader;
+       }
+
+       public void execute(DbImportConfiguration config) throws Exception {
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug("DB connection: " + 
config.getDataSourceInfo());
+               }
+
+               if (logger.isDebugEnabled()) {
+                       logger.debug(config);
+               }
+
+               DataNodeDescriptor dataNodeDescriptor = 
config.createDataNodeDescriptor();
+               DataSource dataSource = 
dataSourceFactory.getDataSource(dataNodeDescriptor);
+               DbAdapter adapter = 
adapterFactory.createAdapter(dataNodeDescriptor, dataSource);
+
+               DataMap loadedFomDb;
+               try (Connection c = dataSource.getConnection()) {
+                       loadedFomDb = load(config, adapter, c);
+               }
+
+               if (loadedFomDb == null) {
+                       logger.info("Nothing was loaded from db.");
+                       return;
+               }
+
+               DataMap existing = loadExistingDataMap(config.getDataMapFile());
+               if (existing == null) {
+                       logger.info("");
+                       File file = config.getDataMapFile();
+                       logger.info("Map file does not exist. Loaded db model 
will be saved into '"
+                                       + (file == null ? "null" : 
file.getAbsolutePath() + "'"));
+
+                       saveLoaded(config.initializeDataMap(loadedFomDb));
+               } else {
+                       MergerFactory mergerFactory = adapter.mergerFactory();
+
+                       List<MergerToken> mergeTokens = new 
DbMerger(mergerFactory).createMergeTokens(existing, loadedFomDb,
+                                       config.getDbLoaderConfig());
+                       if (mergeTokens.isEmpty()) {
+                               logger.info("");
+                               logger.info("Detected changes: No changes to 
import.");
+                               return;
+                       }
+
+                       if (!isBlank(config.getDefaultPackage())) {
+                               
existing.setDefaultPackage(config.getDefaultPackage());
+                       }
+
+                       final Collection<ObjEntity> loadedObjEntities = new 
LinkedList<ObjEntity>();
+                       DataMap executed = execute(new 
ProxyModelMergeDelegate(config.createMergeDelegate()) {
+                               @Override
+                               public void objEntityAdded(ObjEntity ent) {
+                                       loadedObjEntities.add(ent);
+
+                                       super.objEntityAdded(ent);
+                               }
+
+                       }, existing, log(sort(reverse(mergerFactory, 
mergeTokens))));
+
+                       DbLoader.flattenManyToManyRelationships(executed, 
loadedObjEntities, config.getNameGenerator());
+
+                       relationshipsSanity(executed);
+
+                       saveLoaded(executed);
+               }
+       }
+
+       private void relationshipsSanity(DataMap executed) {
+               for (ObjEntity objEntity : executed.getObjEntities()) {
+
+                       List<ObjRelationship> rels = new 
LinkedList<ObjRelationship>(objEntity.getRelationships());
+                       for (ObjRelationship rel : rels) {
+                               if (rel.getSourceEntity() == null || 
rel.getTargetEntity() == null) {
+                                       logger.error("Incorrect obj 
relationship source or target entity is null: " + rel);
+
+                                       
objEntity.removeRelationship(rel.getName());
+                               }
+                       }
+               }
+       }
+
+       protected static List<MergerToken> sort(List<MergerToken> reverse) {
+               Collections.sort(reverse, new Comparator<MergerToken>() {
+                       @Override
+                       public int compare(MergerToken o1, MergerToken o2) {
+                               if (o1 instanceof AddRelationshipToDb && o2 
instanceof AddRelationshipToDb) {
+                                       return 0;
+                               }
+
+                               if (!(o1 instanceof AddRelationshipToDb || o2 
instanceof AddRelationshipToDb)) {
+                                       return 
o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
+                               }
+
+                               return o1 instanceof AddRelationshipToDb ? 1 : 
-1;
+                       }
+               });
+
+               return reverse;
+       }
+
+       private Collection<MergerToken> log(List<MergerToken> tokens) {
+               logger.info("");
+               if (tokens.isEmpty()) {
+                       logger.info("Detected changes: No changes to import.");
+                       return tokens;
+               }
+
+               logger.info("Detected changes: ");
+               for (MergerToken token : tokens) {
+                       logger.info(String.format("    %-20s %s", 
token.getTokenName(), token.getTokenValue()));
+               }
+               logger.info("");
+
+               return tokens;
+       }
+
+       private DataMap loadExistingDataMap(File dataMapFile) throws 
IOException {
+               if (dataMapFile != null && dataMapFile.exists() && 
dataMapFile.canRead()) {
+                       DataMap dataMap = mapLoader.loadDataMap(new 
InputSource(dataMapFile.getCanonicalPath()));
+                       dataMap.setNamespace(new 
EntityResolver(Collections.singleton(dataMap)));
+                       dataMap.setConfigurationSource(new 
URLResource(dataMapFile.toURI().toURL()));
+
+                       return dataMap;
+               }
+
+               return null;
+       }
+
+       private List<MergerToken> reverse(MergerFactory mergerFactory, 
Iterable<MergerToken> mergeTokens)
+                       throws IOException {
+               List<MergerToken> tokens = new LinkedList<MergerToken>();
+               for (MergerToken token : mergeTokens) {
+                       if (token instanceof AbstractToModelToken) {
+                               continue;
+                       }
+                       tokens.add(token.createReverse(mergerFactory));
+               }
+               return tokens;
+       }
+
+       /**
+        * Performs configured schema operations via DbGenerator.
+        */
+       private DataMap execute(ModelMergeDelegate mergeDelegate, DataMap 
dataMap, Collection<MergerToken> tokens) {
+               MergerContext mergerContext = new 
ExecutingMergerContext(dataMap, null, null, mergeDelegate);
+
+               for (MergerToken tok : tokens) {
+                       try {
+                               tok.execute(mergerContext);
+                       } catch (Throwable th) {
+                               String message = "Migration Error. Can't apply 
changes from token: " + tok.getTokenName() + " ("
+                                               + tok.getTokenValue() + ")";
+
+                               logger.error(message, th);
+                               
mergerContext.getValidationResult().addFailure(new SimpleValidationFailure(th, 
message));
+                       }
+               }
+
+               ValidationResult failures = mergerContext.getValidationResult();
+               if (failures == null || !failures.hasFailures()) {
+                       logger.info("Migration Complete Successfully.");
+               } else {
+                       logger.info("Migration Complete.");
+                       logger.warn("Migration finished. The following 
problem(s) were ignored.");
+                       for (ValidationFailure failure : 
failures.getFailures()) {
+                               logger.warn(failure.toString());
+                       }
+               }
+
+               return dataMap;
+       }
+
+       void saveLoaded(DataMap dataMap) throws FileNotFoundException {
+               ConfigurationTree<DataMap> projectRoot = new 
ConfigurationTree<DataMap>(dataMap);
+               Project project = new Project(projectRoot);
+               projectSaver.save(project);
+       }
+
+       private DataMap load(DbImportConfiguration config, DbAdapter adapter, 
Connection connection) throws Exception {
+               DataMap dataMap = config.createDataMap();
+
+               DbLoader loader = config.createLoader(adapter, connection, 
config.createLoaderDelegate());
+               loader.load(dataMap, config.getDbLoaderConfig());
+
+               return dataMap;
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java
 
b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java
index df24bdb..40877b8 100644
--- 
a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java
+++ 
b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskCrossMapRelationshipsTest.java
@@ -38,142 +38,120 @@ import static org.junit.Assert.fail;
 
 public class CayenneGeneratorTaskCrossMapRelationshipsTest {
 
-    /**
-     * Tests pairs generation with a cross-DataMap relationship.
-     */
-    @Test
-    public void testCrossDataMapRelationships() throws Exception {
-
-        CayenneGeneratorTask task = new CayenneGeneratorTask();
-        task.setProject(new Project());
-        task.setTaskName("Test");
-        task.setLocation(Location.UNKNOWN_LOCATION);
-
-        // prepare destination directory
-
-        File destDir = new File(FileUtil.baseTestDirectory(), "cgen12");
-        // prepare destination directory
-        if (!destDir.exists()) {
-            assertTrue(destDir.mkdirs());
-        }
-
-        File map = new File(destDir, "cgen-dependent.map.xml");
-        ResourceUtil.copyResourceToFile(
-                "org/apache/cayenne/tools/cgen-dependent.map.xml",
-                map);
-
-        File additionalMaps[] = new File[1];
-        additionalMaps[0] = new File(destDir, "cgen.map.xml");
-        ResourceUtil.copyResourceToFile(
-                "org/apache/cayenne/tools/cgen.map.xml",
-                additionalMaps[0]);
-
-        FileList additionalMapsFilelist = new FileList();
-        additionalMapsFilelist.setDir(additionalMaps[0].getParentFile());
-        additionalMapsFilelist.setFiles(additionalMaps[0].getName());
-
-        Path additionalMapsPath = new Path(task.getProject());
-        additionalMapsPath.addFilelist(additionalMapsFilelist);
-
-        // setup task
-        task.setMap(map);
-        task.setAdditionalMaps(additionalMapsPath);
-        task.setMakepairs(true);
-        task.setOverwrite(false);
-        task.setMode("entity");
-        task.setIncludeEntities("MyArtGroup");
-        task.setDestDir(destDir);
-        task.setSuperpkg("org.apache.cayenne.testdo.cgen2.auto");
-        task.setUsepkgpath(true);
-
-        // run task
-        task.execute();
-
-        // check results
-        File a = new File(destDir, 
convertPath("org/apache/cayenne/testdo/cgen2/MyArtGroup.java"));
-        assertTrue(a.isFile());
-        assertContents(a, "MyArtGroup", "org.apache.cayenne.testdo.cgen2", 
"_MyArtGroup");
-
-        File _a = new File(destDir, 
convertPath("org/apache/cayenne/testdo/cgen2/auto/_MyArtGroup.java"));
-        assertTrue(_a.exists());
-        assertContents(_a, "_MyArtGroup", 
"org.apache.cayenne.testdo.cgen2.auto", "CayenneDataObject");
-        assertContents(_a, "import 
org.apache.cayenne.testdo.testmap.ArtGroup;");
-        assertContents(_a, " ArtGroup getToParentGroup()");
-        assertContents(_a, "setToParentGroup(ArtGroup toParentGroup)");
-    }
-
-    private String convertPath(String unixPath) {
-        return unixPath.replace('/', File.separatorChar);
-    }
-
-    private void assertContents(File f, String content) throws Exception {
-
-        BufferedReader in = new BufferedReader(new InputStreamReader(new 
FileInputStream(
-                f)));
-
-        try {
-            String s = null;
-            while ((s = in.readLine()) != null) {
-                if (s.contains(content))
-                    return;
-            }
-
-            fail("<" + content + "> not found in " + f.getAbsolutePath() + 
".");
-        }
-        finally {
-            in.close();
-        }
-
-    }
-
-    private void assertContents(
-            File f,
-            String className,
-            String packageName,
-            String extendsName) throws Exception {
-
-        BufferedReader in = new BufferedReader(new InputStreamReader(new 
FileInputStream(
-                f)));
-
-        try {
-            assertPackage(in, packageName);
-            assertClass(in, className, extendsName);
-        }
-        finally {
-            in.close();
-        }
-
-    }
-
-    private void assertPackage(BufferedReader in, String packageName) throws 
Exception {
-
-        String s = null;
-        while ((s = in.readLine()) != null) {
-
-            if (Pattern.matches("^package\\s+([^\\s;]+);", s)) {
-                assertTrue(s.contains(packageName));
-                return;
-            }
-        }
-
-        fail("No package declaration found.");
-    }
-
-    private void assertClass(BufferedReader in, String className, String 
extendsName)
-            throws Exception {
-        
-        Pattern classPattern = Pattern.compile("^public\\s+");
-
-        String s = null;
-        while ((s = in.readLine()) != null) {
-            if (classPattern.matcher(s).find()) {
-                assertTrue(s.contains(className));
-                assertTrue(s.contains(extendsName));
-                assertTrue(s.indexOf(className) < s.indexOf(extendsName));
-                return;
-            }
-        }
-
-        fail("No class declaration found.");
-    }
+       /**
+        * Tests pairs generation with a cross-DataMap relationship.
+        */
+       @Test
+       public void testCrossDataMapRelationships() throws Exception {
+
+               CayenneGeneratorTask task = new CayenneGeneratorTask();
+               task.setProject(new Project());
+               task.setTaskName("Test");
+               task.setLocation(Location.UNKNOWN_LOCATION);
+
+               // prepare destination directory
+
+               File destDir = new File(FileUtil.baseTestDirectory(), "cgen12");
+               // prepare destination directory
+               if (!destDir.exists()) {
+                       assertTrue(destDir.mkdirs());
+               }
+
+               File map = new File(destDir, "cgen-dependent.map.xml");
+               
ResourceUtil.copyResourceToFile("org/apache/cayenne/tools/cgen-dependent.map.xml",
 map);
+
+               File additionalMaps[] = new File[1];
+               additionalMaps[0] = new File(destDir, "cgen.map.xml");
+               
ResourceUtil.copyResourceToFile("org/apache/cayenne/tools/cgen.map.xml", 
additionalMaps[0]);
+
+               FileList additionalMapsFilelist = new FileList();
+               
additionalMapsFilelist.setDir(additionalMaps[0].getParentFile());
+               additionalMapsFilelist.setFiles(additionalMaps[0].getName());
+
+               Path additionalMapsPath = new Path(task.getProject());
+               additionalMapsPath.addFilelist(additionalMapsFilelist);
+
+               // setup task
+               task.setMap(map);
+               task.setAdditionalMaps(additionalMapsPath);
+               task.setMakepairs(true);
+               task.setOverwrite(false);
+               task.setMode("entity");
+               task.setIncludeEntities("MyArtGroup");
+               task.setDestDir(destDir);
+               task.setSuperpkg("org.apache.cayenne.testdo.cgen2.auto");
+               task.setUsepkgpath(true);
+
+               // run task
+               task.execute();
+
+               // check results
+               File a = new File(destDir, 
convertPath("org/apache/cayenne/testdo/cgen2/MyArtGroup.java"));
+               assertTrue(a.isFile());
+               assertContents(a, "MyArtGroup", 
"org.apache.cayenne.testdo.cgen2", "_MyArtGroup");
+
+               File _a = new File(destDir, 
convertPath("org/apache/cayenne/testdo/cgen2/auto/_MyArtGroup.java"));
+               assertTrue(_a.exists());
+               assertContents(_a, "_MyArtGroup", 
"org.apache.cayenne.testdo.cgen2.auto", "CayenneDataObject");
+               assertContents(_a, "import 
org.apache.cayenne.testdo.testmap.ArtGroup;");
+               assertContents(_a, " ArtGroup getToParentGroup()");
+               assertContents(_a, "setToParentGroup(ArtGroup toParentGroup)");
+       }
+
+       private String convertPath(String unixPath) {
+               return unixPath.replace('/', File.separatorChar);
+       }
+
+       private void assertContents(File f, String content) throws Exception {
+
+               try (BufferedReader in = new BufferedReader(new 
InputStreamReader(new FileInputStream(f)));) {
+                       String s = null;
+                       while ((s = in.readLine()) != null) {
+                               if (s.contains(content))
+                                       return;
+                       }
+
+                       fail("<" + content + "> not found in " + 
f.getAbsolutePath() + ".");
+               }
+
+       }
+
+       private void assertContents(File f, String className, String 
packageName, String extendsName) throws Exception {
+
+               try (BufferedReader in = new BufferedReader(new 
InputStreamReader(new FileInputStream(f)));) {
+                       assertPackage(in, packageName);
+                       assertClass(in, className, extendsName);
+               }
+       }
+
+       private void assertPackage(BufferedReader in, String packageName) 
throws Exception {
+
+               String s = null;
+               while ((s = in.readLine()) != null) {
+
+                       if (Pattern.matches("^package\\s+([^\\s;]+);", s)) {
+                               assertTrue(s.contains(packageName));
+                               return;
+                       }
+               }
+
+               fail("No package declaration found.");
+       }
+
+       private void assertClass(BufferedReader in, String className, String 
extendsName) throws Exception {
+
+               Pattern classPattern = Pattern.compile("^public\\s+");
+
+               String s = null;
+               while ((s = in.readLine()) != null) {
+                       if (classPattern.matcher(s).find()) {
+                               assertTrue(s.contains(className));
+                               assertTrue(s.contains(extendsName));
+                               assertTrue(s.indexOf(className) < 
s.indexOf(extendsName));
+                               return;
+                       }
+               }
+
+               fail("No class declaration found.");
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java
 
b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java
index 09c954d..f6f616b 100644
--- 
a/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java
+++ 
b/cayenne-tools/src/test/java/org/apache/cayenne/tools/CayenneGeneratorTaskTest.java
@@ -38,359 +38,280 @@ import static org.junit.Assert.fail;
 
 public class CayenneGeneratorTaskTest {
 
-    private static final File baseDir;
-    private static final File map;
-    private static final File mapEmbeddables;
-    private static final File template;
-
-    static {
-
-        baseDir = FileUtil.baseTestDirectory();
-        map = new File(baseDir, "antmap.xml");
-        mapEmbeddables = new File(baseDir, "antmap-embeddables.xml");
-        template = new File(baseDir, "velotemplate.vm");
-
-        ResourceUtil.copyResourceToFile("testmap.map.xml", map);
-        ResourceUtil.copyResourceToFile("embeddable.map.xml", mapEmbeddables);
-        ResourceUtil.copyResourceToFile(
-                "org/apache/cayenne/tools/velotemplate.vm",
-                template);
-    }
-
-    protected CayenneGeneratorTask task;
-
-    @Before
-    public void setUp() {
-
-        Project project = new Project();
-        project.setBaseDir(baseDir);
-
-        task = new CayenneGeneratorTask();
-        task.setProject(project);
-        task.setTaskName("Test");
-        task.setLocation(Location.UNKNOWN_LOCATION);
-    }
-
-    /**
-     * Test single classes with a non-standard template.
-     */
-    @Test
-    public void testSingleClassesCustTemplate() throws Exception {
-        // prepare destination directory
-        File mapDir = new File(baseDir, "single-classes-custtempl");
-        assertTrue(mapDir.mkdirs());
-
-        // setup task
-        task.setDestDir(mapDir);
-        task.setMap(map);
-        task.setMakepairs(false);
-        task.setUsepkgpath(true);
-        task.setTemplate(template.getPath());
-
-        // run task
-        task.execute();
-
-        // check results
-        File a = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
-        assertTrue(a.isFile());
-        assertContents(
-                a,
-                "Artist",
-                "org.apache.cayenne.testdo.testmap",
-                "CayenneDataObject");
-
-        File _a = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/_Artist.java"));
-        assertFalse(_a.exists());
-    }
-
-    /** Test single classes generation including full package path. */
-    @Test
-    public void testSingleClasses1() throws Exception {
-        // prepare destination directory
-        File mapDir = new File(baseDir, "single-classes-tree");
-        assertTrue(mapDir.mkdirs());
-
-        // setup task
-        task.setDestDir(mapDir);
-        task.setMap(map);
-        task.setMakepairs(false);
-        task.setUsepkgpath(true);
-
-        // run task
-        task.execute();
-
-        // check results
-        File a = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
-        assertTrue(a.isFile());
-        assertContents(
-                a,
-                "Artist",
-                "org.apache.cayenne.testdo.testmap",
-                "CayenneDataObject");
-
-        File _a = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/_Artist.java"));
-        assertFalse(_a.exists());
-    }
-
-    /** Test single classes generation ignoring package path. */
-    @Test
-    public void testSingleClasses2() throws Exception {
-        // prepare destination directory
-        File mapDir = new File(baseDir, "single-classes-flat");
-        assertTrue(mapDir.mkdirs());
-
-        // setup task
-        task.setDestDir(mapDir);
-        task.setMap(map);
-        task.setMakepairs(false);
-        task.setUsepkgpath(false);
-
-        // run task
-        task.execute();
-
-        // check results
-        File a = new File(mapDir, convertPath("Artist.java"));
-        assertTrue(a.exists());
-        assertContents(
-                a,
-                "Artist",
-                "org.apache.cayenne.testdo.testmap",
-                "CayenneDataObject");
-
-        File _a = new File(mapDir, convertPath("_Artist.java"));
-        assertFalse(_a.exists());
-
-        File pkga = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
-        assertFalse(pkga.exists());
-    }
-
-    /** Test pairs generation including full package path, default superclass 
package. */
-    @Test
-    public void testPairs1() throws Exception {
-        // prepare destination directory
-        File mapDir = new File(baseDir, "pairs-tree");
-        assertTrue(mapDir.mkdirs());
-
-        // setup task
-        task.setDestDir(mapDir);
-        task.setMap(map);
-        task.setMakepairs(true);
-        task.setUsepkgpath(true);
-
-        // run task
-        task.execute();
-
-        // check results
-        File a = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
-        assertTrue(a.isFile());
-        assertContents(a, "Artist", "org.apache.cayenne.testdo.testmap", 
"_Artist");
-
-        File _a = new File(
-                mapDir,
-                
convertPath("org/apache/cayenne/testdo/testmap/auto/_Artist.java"));
-        assertTrue(_a.exists());
-        assertContents(
-                _a,
-                "_Artist",
-                "org.apache.cayenne.testdo.testmap",
-                "CayenneDataObject");
-    }
-
-    /** Test pairs generation in the same directory. */
-    @Test
-    public void testPairs2() throws Exception {
-        // prepare destination directory
-        File mapDir = new File(baseDir, "pairs-flat");
-        assertTrue(mapDir.mkdirs());
-
-        // setup task
-        task.setDestDir(mapDir);
-        task.setMap(map);
-        task.setMakepairs(true);
-        task.setUsepkgpath(false);
-
-        // run task
-        task.execute();
-
-        // check results
-        File a = new File(mapDir, convertPath("Artist.java"));
-        assertTrue(a.isFile());
-        assertContents(a, "Artist", "org.apache.cayenne.testdo.testmap", 
"_Artist");
-
-        File _a = new File(mapDir, convertPath("_Artist.java"));
-        assertTrue(_a.exists());
-        assertContents(
-                _a,
-                "_Artist",
-                "org.apache.cayenne.testdo.testmap",
-                "CayenneDataObject");
-
-        File pkga = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
-        assertFalse(pkga.exists());
-    }
-
-    /**
-     * Test pairs generation including full package path with superclass and 
subclass in
-     * different packages.
-     */
-    @Test
-    public void testPairs3() throws Exception {
-        // prepare destination directory
-        File mapDir = new File(baseDir, "pairs-tree-split");
-        assertTrue(mapDir.mkdirs());
-
-        // setup task
-        task.setDestDir(mapDir);
-        task.setMap(map);
-        task.setMakepairs(true);
-        task.setUsepkgpath(true);
-        task.setSuperpkg("org.apache.cayenne.testdo.testmap.superart");
-
-        // run task
-        task.execute();
-
-        // check results
-        File a = new File(
-                mapDir,
-                convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
-        assertTrue(a.isFile());
-        assertContents(a, "Artist", "org.apache.cayenne.testdo.testmap", 
"_Artist");
-
-        File _a = new File(
-                mapDir,
-                
convertPath("org/apache/cayenne/testdo/testmap/superart/_Artist.java"));
-        assertTrue(_a.exists());
-        assertContents(
-                _a,
-                "_Artist",
-                "org.apache.cayenne.testdo.testmap.superart",
-                "CayenneDataObject");
-    }
-
-    @Test
-    public void testPairsEmbeddable3() throws Exception {
-        // prepare destination directory
-        File mapDir = new File(baseDir, "pairs-embeddables3-split");
-        assertTrue(mapDir.mkdirs());
-
-        // setup task
-        task.setDestDir(mapDir);
-        task.setMap(mapEmbeddables);
-        task.setMakepairs(true);
-        task.setUsepkgpath(true);
-        task.setSuperpkg("org.apache.cayenne.testdo.embeddable.auto");
-
-        // run task
-        task.execute();
-
-        // check entity results
-        File a = new File(
-                mapDir,
-                
convertPath("org/apache/cayenne/testdo/embeddable/EmbedEntity1.java"));
-        assertTrue(a.isFile());
-        assertContents(
-                a,
-                "EmbedEntity1",
-                "org.apache.cayenne.testdo.embeddable",
-                "_EmbedEntity1");
-
-        File _a = new File(
-                mapDir,
-                
convertPath("org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java"));
-        assertTrue(_a.exists());
-        assertContents(
-                _a,
-                "_EmbedEntity1",
-                "org.apache.cayenne.testdo.embeddable.auto",
-                "CayenneDataObject");
-
-        // check embeddable results
-        File e = new File(
-                mapDir,
-                
convertPath("org/apache/cayenne/testdo/embeddable/Embeddable1.java"));
-        assertTrue(e.isFile());
-        assertContents(
-                e,
-                "Embeddable1",
-                "org.apache.cayenne.testdo.embeddable",
-                "_Embeddable1");
-
-        File _e = new File(
-                mapDir,
-                
convertPath("org/apache/cayenne/testdo/embeddable/auto/_Embeddable1.java"));
-        assertTrue(_e.exists());
-        assertContents(
-                _e,
-                "_Embeddable1",
-                "org.apache.cayenne.testdo.embeddable.auto",
-                "Object");
-    }
-
-    private String convertPath(String unixPath) {
-        return unixPath.replace('/', File.separatorChar);
-    }
-
-    private void assertContents(
-            File f,
-            String className,
-            String packageName,
-            String extendsName) throws Exception {
-
-        BufferedReader in = new BufferedReader(new InputStreamReader(new 
FileInputStream(
-                f)));
-
-        try {
-            assertPackage(in, packageName);
-            assertClass(in, className, extendsName);
-        }
-        finally {
-            in.close();
-        }
-
-    }
-
-    private void assertPackage(BufferedReader in, String packageName) throws 
Exception {
-
-        String s = null;
-        while ((s = in.readLine()) != null) {
-            if (Pattern.matches("^package\\s+([^\\s;]+);", s)) {
-                assertTrue(s.indexOf(packageName) > 0);
-                return;
-            }
-        }
-
-        fail("No package declaration found.");
-    }
-
-    private void assertClass(BufferedReader in, String className, String 
extendsName)
-            throws Exception {
-
-        Pattern classPattern = Pattern.compile("^public\\s+");
-
-        String s = null;
-        while ((s = in.readLine()) != null) {
-            if (classPattern.matcher(s).find()) {
-                assertTrue(s.indexOf(className) > 0);
-                assertTrue(s.indexOf(extendsName) > 0);
-                assertTrue(s.indexOf(className) < s.indexOf(extendsName));
-                return;
-            }
-        }
-
-        fail("No class declaration found.");
-    }
+       private static final File baseDir;
+       private static final File map;
+       private static final File mapEmbeddables;
+       private static final File template;
+
+       static {
+
+               baseDir = FileUtil.baseTestDirectory();
+               map = new File(baseDir, "antmap.xml");
+               mapEmbeddables = new File(baseDir, "antmap-embeddables.xml");
+               template = new File(baseDir, "velotemplate.vm");
+
+               ResourceUtil.copyResourceToFile("testmap.map.xml", map);
+               ResourceUtil.copyResourceToFile("embeddable.map.xml", 
mapEmbeddables);
+               
ResourceUtil.copyResourceToFile("org/apache/cayenne/tools/velotemplate.vm", 
template);
+       }
+
+       protected CayenneGeneratorTask task;
+
+       @Before
+       public void setUp() {
+
+               Project project = new Project();
+               project.setBaseDir(baseDir);
+
+               task = new CayenneGeneratorTask();
+               task.setProject(project);
+               task.setTaskName("Test");
+               task.setLocation(Location.UNKNOWN_LOCATION);
+       }
+
+       /**
+        * Test single classes with a non-standard template.
+        */
+       @Test
+       public void testSingleClassesCustTemplate() throws Exception {
+               // prepare destination directory
+               File mapDir = new File(baseDir, "single-classes-custtempl");
+               assertTrue(mapDir.mkdirs());
+
+               // setup task
+               task.setDestDir(mapDir);
+               task.setMap(map);
+               task.setMakepairs(false);
+               task.setUsepkgpath(true);
+               task.setTemplate(template.getPath());
+
+               // run task
+               task.execute();
+
+               // check results
+               File a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
+               assertTrue(a.isFile());
+               assertContents(a, "Artist", 
"org.apache.cayenne.testdo.testmap", "CayenneDataObject");
+
+               File _a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/_Artist.java"));
+               assertFalse(_a.exists());
+       }
+
+       /** Test single classes generation including full package path. */
+       @Test
+       public void testSingleClasses1() throws Exception {
+               // prepare destination directory
+               File mapDir = new File(baseDir, "single-classes-tree");
+               assertTrue(mapDir.mkdirs());
+
+               // setup task
+               task.setDestDir(mapDir);
+               task.setMap(map);
+               task.setMakepairs(false);
+               task.setUsepkgpath(true);
+
+               // run task
+               task.execute();
+
+               // check results
+               File a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
+               assertTrue(a.isFile());
+               assertContents(a, "Artist", 
"org.apache.cayenne.testdo.testmap", "CayenneDataObject");
+
+               File _a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/_Artist.java"));
+               assertFalse(_a.exists());
+       }
+
+       /** Test single classes generation ignoring package path. */
+       @Test
+       public void testSingleClasses2() throws Exception {
+               // prepare destination directory
+               File mapDir = new File(baseDir, "single-classes-flat");
+               assertTrue(mapDir.mkdirs());
+
+               // setup task
+               task.setDestDir(mapDir);
+               task.setMap(map);
+               task.setMakepairs(false);
+               task.setUsepkgpath(false);
+
+               // run task
+               task.execute();
+
+               // check results
+               File a = new File(mapDir, convertPath("Artist.java"));
+               assertTrue(a.exists());
+               assertContents(a, "Artist", 
"org.apache.cayenne.testdo.testmap", "CayenneDataObject");
+
+               File _a = new File(mapDir, convertPath("_Artist.java"));
+               assertFalse(_a.exists());
+
+               File pkga = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
+               assertFalse(pkga.exists());
+       }
+
+       /**
+        * Test pairs generation including full package path, default superclass
+        * package.
+        */
+       @Test
+       public void testPairs1() throws Exception {
+               // prepare destination directory
+               File mapDir = new File(baseDir, "pairs-tree");
+               assertTrue(mapDir.mkdirs());
+
+               // setup task
+               task.setDestDir(mapDir);
+               task.setMap(map);
+               task.setMakepairs(true);
+               task.setUsepkgpath(true);
+
+               // run task
+               task.execute();
+
+               // check results
+               File a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
+               assertTrue(a.isFile());
+               assertContents(a, "Artist", 
"org.apache.cayenne.testdo.testmap", "_Artist");
+
+               File _a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/auto/_Artist.java"));
+               assertTrue(_a.exists());
+               assertContents(_a, "_Artist", 
"org.apache.cayenne.testdo.testmap", "CayenneDataObject");
+       }
+
+       /** Test pairs generation in the same directory. */
+       @Test
+       public void testPairs2() throws Exception {
+               // prepare destination directory
+               File mapDir = new File(baseDir, "pairs-flat");
+               assertTrue(mapDir.mkdirs());
+
+               // setup task
+               task.setDestDir(mapDir);
+               task.setMap(map);
+               task.setMakepairs(true);
+               task.setUsepkgpath(false);
+
+               // run task
+               task.execute();
+
+               // check results
+               File a = new File(mapDir, convertPath("Artist.java"));
+               assertTrue(a.isFile());
+               assertContents(a, "Artist", 
"org.apache.cayenne.testdo.testmap", "_Artist");
+
+               File _a = new File(mapDir, convertPath("_Artist.java"));
+               assertTrue(_a.exists());
+               assertContents(_a, "_Artist", 
"org.apache.cayenne.testdo.testmap", "CayenneDataObject");
+
+               File pkga = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
+               assertFalse(pkga.exists());
+       }
+
+       /**
+        * Test pairs generation including full package path with superclass and
+        * subclass in different packages.
+        */
+       @Test
+       public void testPairs3() throws Exception {
+               // prepare destination directory
+               File mapDir = new File(baseDir, "pairs-tree-split");
+               assertTrue(mapDir.mkdirs());
+
+               // setup task
+               task.setDestDir(mapDir);
+               task.setMap(map);
+               task.setMakepairs(true);
+               task.setUsepkgpath(true);
+               task.setSuperpkg("org.apache.cayenne.testdo.testmap.superart");
+
+               // run task
+               task.execute();
+
+               // check results
+               File a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/Artist.java"));
+               assertTrue(a.isFile());
+               assertContents(a, "Artist", 
"org.apache.cayenne.testdo.testmap", "_Artist");
+
+               File _a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/testmap/superart/_Artist.java"));
+               assertTrue(_a.exists());
+               assertContents(_a, "_Artist", 
"org.apache.cayenne.testdo.testmap.superart", "CayenneDataObject");
+       }
+
+       @Test
+       public void testPairsEmbeddable3() throws Exception {
+               // prepare destination directory
+               File mapDir = new File(baseDir, "pairs-embeddables3-split");
+               assertTrue(mapDir.mkdirs());
+
+               // setup task
+               task.setDestDir(mapDir);
+               task.setMap(mapEmbeddables);
+               task.setMakepairs(true);
+               task.setUsepkgpath(true);
+               task.setSuperpkg("org.apache.cayenne.testdo.embeddable.auto");
+
+               // run task
+               task.execute();
+
+               // check entity results
+               File a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/embeddable/EmbedEntity1.java"));
+               assertTrue(a.isFile());
+               assertContents(a, "EmbedEntity1", 
"org.apache.cayenne.testdo.embeddable", "_EmbedEntity1");
+
+               File _a = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java"));
+               assertTrue(_a.exists());
+               assertContents(_a, "_EmbedEntity1", 
"org.apache.cayenne.testdo.embeddable.auto", "CayenneDataObject");
+
+               // check embeddable results
+               File e = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/embeddable/Embeddable1.java"));
+               assertTrue(e.isFile());
+               assertContents(e, "Embeddable1", 
"org.apache.cayenne.testdo.embeddable", "_Embeddable1");
+
+               File _e = new File(mapDir, 
convertPath("org/apache/cayenne/testdo/embeddable/auto/_Embeddable1.java"));
+               assertTrue(_e.exists());
+               assertContents(_e, "_Embeddable1", 
"org.apache.cayenne.testdo.embeddable.auto", "Object");
+       }
+
+       private String convertPath(String unixPath) {
+               return unixPath.replace('/', File.separatorChar);
+       }
+
+       private void assertContents(File f, String className, String 
packageName, String extendsName) throws Exception {
+
+               try (BufferedReader in = new BufferedReader(new 
InputStreamReader(new FileInputStream(f)));) {
+                       assertPackage(in, packageName);
+                       assertClass(in, className, extendsName);
+               }
+       }
+
+       private void assertPackage(BufferedReader in, String packageName) 
throws Exception {
+
+               String s = null;
+               while ((s = in.readLine()) != null) {
+                       if (Pattern.matches("^package\\s+([^\\s;]+);", s)) {
+                               assertTrue(s.indexOf(packageName) > 0);
+                               return;
+                       }
+               }
+
+               fail("No package declaration found.");
+       }
+
+       private void assertClass(BufferedReader in, String className, String 
extendsName) throws Exception {
+
+               Pattern classPattern = Pattern.compile("^public\\s+");
+
+               String s = null;
+               while ((s = in.readLine()) != null) {
+                       if (classPattern.matcher(s).find()) {
+                               assertTrue(s.indexOf(className) > 0);
+                               assertTrue(s.indexOf(extendsName) > 0);
+                               assertTrue(s.indexOf(className) < 
s.indexOf(extendsName));
+                               return;
+                       }
+               }
+
+               fail("No class declaration found.");
+       }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-tools/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-tools/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java 
b/cayenne-tools/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java
index ce44aa7..075d88d 100644
--- 
a/cayenne-tools/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java
+++ 
b/cayenne-tools/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java
@@ -88,10 +88,10 @@ public class DbImporterTaskTest {
                
assertSkipRelationshipsLoading(getCdbImport("build-skip-relationships-loading.xml").getReverseEngineering());
        }
 
-    @Test
-    public void testTableTypes() throws Exception {
-        
assertTableTypes(getCdbImport("build-table-types.xml").getReverseEngineering());
-    }
+       @Test
+       public void testTableTypes() throws Exception {
+               
assertTableTypes(getCdbImport("build-table-types.xml").getReverseEngineering());
+       }
 
        @Test
        public void testIncludeTable() throws Exception {
@@ -190,14 +190,13 @@ public class DbImporterTaskTest {
 
                        DetailedDiff diff = new DetailedDiff(new Diff(control, 
test));
                        if (!diff.similar()) {
-                for (Difference d : ((List<Difference>) 
diff.getAllDifferences())) {
+                               for (Difference d : ((List<Difference>) 
diff.getAllDifferences())) {
 
-
-                    
System.out.println("-------------------------------------------");
-                    System.out.println(d.getTestNodeDetail().getNode());
-                    System.out.println(d.getControlNodeDetail().getValue());
-                }
-                fail(diff.toString());
+                                       
System.out.println("-------------------------------------------");
+                                       
System.out.println(d.getTestNodeDetail().getNode());
+                                       
System.out.println(d.getControlNodeDetail().getValue());
+                               }
+                               fail(diff.toString());
                        }
 
                } catch (SAXException e) {
@@ -216,16 +215,13 @@ public class DbImporterTaskTest {
 
                Class.forName(dbImportConfiguration.getDriver()).newInstance();
 
-               Connection c = 
DriverManager.getConnection(dbImportConfiguration.getUrl());
-               try {
-
-                       Statement stmt = c.createStatement();
+               try (Connection c = 
DriverManager.getConnection(dbImportConfiguration.getUrl());) {
 
                        // TODO: move parsing SQL files to a common utility 
(DBHelper?) .
                        // ALso see UnitDbApater.executeDDL - this should use 
the same
                        // utility
 
-                       try {
+                       try (Statement stmt = c.createStatement();) {
                                for (String sql : SQLReader.statements(sqlUrl, 
";")) {
 
                                        // skip comments
@@ -235,11 +231,7 @@ public class DbImporterTaskTest {
 
                                        stmt.execute(sql);
                                }
-                       } finally {
-                               stmt.close();
                        }
-               } finally {
-                       c.close();
                }
        }
 

Reply via email to