cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject [04/17] cayenne git commit: CAY-2026 Java 7
Date Sat, 12 Sep 2015 10:02:32 GMT
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();
 		}
 	}
 


Mime
View raw message