Ok, so I have finished my implementation of a "pair" pojo exporter.
My ant file looks like so:
Code:
<hbm2java ejb3="true" jdk5="true">
<base transform="{package-name}/base/{class-name}Base"/>
<concrete transform="{package-name}/{class-name}"/>
</hbm2java>
I chose to do it this way (with a new exporter and new sub-tasks) because for the concrete part, I needed access to the base class name, or I didn't know what to extend.
The transform attribute tells the exporter how to change the class name. It is just like the filePattern attribute (but it describes a class name instead of a file).
To make this happen, I extended POJOExporter.java to export, first all of the compontents, then the entities, twice, once for the base class, then another for the concrete class. I changed the class info via overriding the meta attributes on the EntityPOJOClass. Also, I added "basePartOfPair" and "concretePartOfPair" to the template context so that I would do the stuff in the templates.
I had hoped to make the exporter a bit more cleanly, but I found that pretty anything else would require hacking the configuration classes.
And now for the actual patch. Looks like there aren't any attachements in this forums implemenation, so I will just put it in here. It is against the "Branch_3_2" of HibernateExt, which I think is the way to go.
Let me know there are some changes you want, or if you don't like something. I would be happy to work with it a bit. Let me know what you think.
Code:
Index: tools/src/java/org/hibernate/tool/ant/Hbm2JavaExporterTask.java
===================================================================
--- tools/src/java/org/hibernate/tool/ant/Hbm2JavaExporterTask.java (revision 15916)
+++ tools/src/java/org/hibernate/tool/ant/Hbm2JavaExporterTask.java (working copy)
@@ -4,8 +4,12 @@
*/
package org.hibernate.tool.ant;
+import java.util.ArrayList;
+import java.util.List;
+
import org.hibernate.tool.hbm2x.Exporter;
import org.hibernate.tool.hbm2x.POJOExporter;
+import org.hibernate.tool.hbm2x.POJOPairExporter;
/**
* @author max
@@ -16,6 +20,7 @@
boolean ejb3 = false;
boolean jdk5 = false;
+ List<PojoPairConfig> pairConfigs = new ArrayList<PojoPairConfig>(2);
public Hbm2JavaExporterTask(HibernateToolTask parent) {
super( parent );
@@ -28,7 +33,44 @@
public void setJdk5(boolean b) {
jdk5 = b;
}
+
+ public PojoPairConfig createConcrete() {
+ ConcreteConfig c = new ConcreteConfig();
+ pairConfigs.add(c);
+ return c;
+ }
+
+ public PojoPairConfig createBase() {
+ BaseConfig c = new BaseConfig();
+ pairConfigs.add(c);
+ return c;
+ }
+
+ public static abstract class PojoPairConfig {
+ String transform;
+ boolean overwrite;
+ public void setTransform( String transform ) {
+ this.transform = transform;
+ }
+ public void setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ }
+ public String getTransform() {
+ return this.transform;
+ }
+ public boolean getOverwrite() {
+ return this.overwrite;
+ }
+ }
+
+ public static class ConcreteConfig extends PojoPairConfig {
+
+ }
+ public static class BaseConfig extends PojoPairConfig {
+
+ }
+
protected Exporter configureExporter(Exporter exp) {
POJOExporter exporter = (POJOExporter) exp;
super.configureExporter( exp );
@@ -38,6 +80,9 @@
}
protected Exporter createExporter() {
+ if ( pairConfigs != null && pairConfigs.size() > 1 ) {
+ return new POJOPairExporter(pairConfigs);
+ }
return new POJOExporter();
}
Index: tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java
===================================================================
--- tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java (revision 15916)
+++ tools/src/java/org/hibernate/tool/hbm2x/GenericExporter.java (working copy)
@@ -148,8 +148,8 @@
producer.produce(additionalContext, getTemplateName(), new File(getOutputDirectory(),filename), templateName, element.toString());
}
- protected String resolveFilename(POJOClass element) {
- String filename = StringHelper.replace(filePattern, "{class-name}", getClassNameForFile( element ));
+ protected String resolvePattern(String pattern, POJOClass element) {
+ String filename = StringHelper.replace(pattern, "{class-name}", getClassNameForFile( element ));
String packageLocation = StringHelper.replace(getPackageNameForFile( element ),".", "/");
if(StringHelper.isEmpty(packageLocation)) {
packageLocation = "."; // done to ensure default package classes doesn't end up in the root of the filesystem when outputdir=""
@@ -157,6 +157,9 @@
filename = StringHelper.replace(filename, "{package-name}", packageLocation);
return filename;
}
+ protected String resolveFilename(POJOClass element) {
+ return resolvePattern( this.filePattern, element );
+ }
protected String getPackageNameForFile(POJOClass element) {
return element.getPackageName();
Index: tools/src/java/org/hibernate/tool/hbm2x/POJOPairExporter.java
===================================================================
--- tools/src/java/org/hibernate/tool/hbm2x/POJOPairExporter.java (revision 0)
+++ tools/src/java/org/hibernate/tool/hbm2x/POJOPairExporter.java (revision 0)
@@ -0,0 +1,184 @@
+package org.hibernate.tool.hbm2x;
+
+import static org.hibernate.tool.hbm2x.MetaAttributeConstants.*;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.MetaAttribute;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.tool.ant.Hbm2JavaExporterTask;
+import org.hibernate.tool.hbm2x.pojo.EntityPOJOClass;
+import org.hibernate.tool.hbm2x.pojo.POJOClass;
+
+public class POJOPairExporter extends POJOExporter {
+ List<Hbm2JavaExporterTask.PojoPairConfig> pairConfigs;
+ Hbm2JavaExporterTask.PojoPairConfig concreteConfig;
+ Hbm2JavaExporterTask.PojoPairConfig baseConfig;
+
+ public POJOPairExporter(Configuration cfg, File outputdir) {
+ super(cfg, outputdir);
+ }
+
+ public POJOPairExporter() {
+ super();
+ }
+
+ public POJOPairExporter( List<Hbm2JavaExporterTask.PojoPairConfig> pairConfigs ) {
+ super();
+ this.pairConfigs = pairConfigs;
+ assignConfigs();
+ }
+
+ protected void assignConfigs() {
+ for ( Hbm2JavaExporterTask.PojoPairConfig c : this.pairConfigs ) {
+ if ( c instanceof Hbm2JavaExporterTask.BaseConfig ) {
+ this.baseConfig = c;
+ } else if ( c instanceof Hbm2JavaExporterTask.ConcreteConfig ) {
+ this.concreteConfig = c;
+ } else {
+ throw new BuildException("we don't support the config: '" + c.getClass().getName() + "'");
+ }
+ }
+ if ( this.concreteConfig == null ) {
+ throw new BuildException("need concrete part for pojo pair to work");
+ }
+ if ( this.baseConfig == null ) {
+ throw new BuildException("need base part for pojo pair to work");
+ }
+ }
+
+ public void setPairConfigs( List<Hbm2JavaExporterTask.PojoPairConfig> pairConfigs ) {
+ this.pairConfigs = pairConfigs;
+ assignConfigs();
+ }
+
+ @Override
+ protected void doStart() {
+ // do components with standard pojo exporter settings
+ ModelIterator componentMI = (ModelIterator) modelIterators.get( "component");
+ componentMI.process( this );
+
+ // base classes of entities
+ ModelIterator entityMI = (ModelIterator) modelIterators.get("entity");
+ getProperties().put("basePartOfPair","true");
+ getProperties().put("concretePartOfPair","false");
+ getTemplateHelper().putInContext( "basePartOfPair", Boolean.TRUE );
+ getTemplateHelper().putInContext( "concretePartOfPair", Boolean.FALSE );
+ entityMI.process( this );
+
+ // concrete classes of entities
+ getProperties().put("basePartOfPair","false");
+ getProperties().put("concretePartOfPair","true");
+ getTemplateHelper().putInContext( "basePartOfPair", Boolean.FALSE );
+ getTemplateHelper().putInContext( "concretePartOfPair", Boolean.TRUE );
+ entityMI.process( this );
+
+ pojoBaseMap.clear();
+ }
+
+ // map the element to the base class name for "extends"
+ Map<String, String> pojoBaseMap = new HashMap<String, String>();
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void exportPersistentClass( Map additionalContext, POJOClass element ) {
+ Map metaAttributes = null;
+ PersistentClass pclz = null;
+ String originalClassName = element.getQualifiedDeclarationName();
+ if ( element instanceof EntityPOJOClass ) {
+ String baseProperty = getProperties().getProperty( "basePartOfPair", "false" );
+ String concreteProperty = getProperties().getProperty( "concretePartOfPair", "false" );
+ if ( Boolean.parseBoolean( baseProperty ) ) {
+ String resolvedPattern = resolvePattern( baseConfig.getTransform(), element );
+ resolvedPattern = resolvedPattern.replace( '/', '.' );
+
+ EntityPOJOClass clz = (EntityPOJOClass) element;
+ pclz = (PersistentClass) clz.getDecoratedObject();
+ MetaAttribute ma = new MetaAttribute("scope-field");
+ ma.addValue( "protected" );
+ metaAttributes = pclz.getMetaAttributes();
+ metaAttributes.put( "scope-field", ma );
+
+ ma = new MetaAttribute(SCOPE_CLASS);
+ ma.addValue( "public abstract" );
+ metaAttributes.put( SCOPE_CLASS, ma );
+
+ ma = new MetaAttribute(GENERATED_CLASS);
+ ma.addValue( resolvedPattern );
+ metaAttributes.put( GENERATED_CLASS, ma );
+
+ pojoBaseMap.put( originalClassName, resolvedPattern );
+ } else if ( Boolean.parseBoolean( concreteProperty ) ) {
+ String resolvedPattern = resolvePattern( concreteConfig.getTransform(), element );
+ resolvedPattern = resolvedPattern.replace( '/', '.' );
+
+ EntityPOJOClass clz = (EntityPOJOClass) element;
+ pclz = (PersistentClass) clz.getDecoratedObject();
+
+ MetaAttribute ma;
+ metaAttributes = pclz.getMetaAttributes();
+ ma = new MetaAttribute(GENERATED_CLASS);
+ ma.addValue( resolvedPattern );
+ metaAttributes.put( GENERATED_CLASS, ma );
+
+ ma = new MetaAttribute(GEN_PROPERTY);
+ ma.addValue( "false" );
+ metaAttributes.put( GEN_PROPERTY, ma );
+
+ String baseClass = pojoBaseMap.remove(originalClassName);
+ if ( baseClass == null ) {
+ log.error( "didn't find '" + originalClassName + "' in the list of base classes." );
+ }
+ ma = new MetaAttribute(EXTENDS);
+ ma.addValue( baseClass );
+ metaAttributes.put( EXTENDS, ma);
+ }
+ }
+ super.exportPersistentClass( additionalContext, element );
+ // reset everything to the default
+ if ( metaAttributes != null ) {
+ metaAttributes.remove( EXTENDS );
+ metaAttributes.remove( GEN_PROPERTY );
+ metaAttributes.remove( GENERATED_CLASS );
+ metaAttributes.remove( SCOPE_CLASS );
+ metaAttributes.remove( "scope-field" );
+ }
+ }
+ protected void doxStart() {
+ setForEach("component");
+ super.doStart();
+
+ for (Hbm2JavaExporterTask.PojoPairConfig config : pairConfigs) {
+// setFilePattern(config.getFilePattern());
+ setForEach( "entity" );
+ if ( config instanceof Hbm2JavaExporterTask.BaseConfig ) {
+ getTemplateHelper().putInContext( "basePartOfPair", Boolean.TRUE );
+ getTemplateHelper().putInContext( "concretePartOfPair", Boolean.FALSE );
+ getProperties().put("basePartOfPair","true");
+ getProperties().put("concretePartOfPair","false");
+ } else {
+ getTemplateHelper().putInContext( "basePartOfPair", Boolean.FALSE );
+ getTemplateHelper().putInContext( "concretePartOfPair", Boolean.TRUE );
+ getProperties().put("basePartOfPair","false");
+ getProperties().put("concretePartOfPair","true");
+ }
+ super.doStart();
+ }
+ }
+
+ @Override
+ protected void setupContext() {
+ if ( !getProperties().containsKey( "basePartOfPair" )) {
+ getProperties().put("basePartOfPair","false");
+ }
+ if ( !getProperties().containsKey( "concretePartOfPair" )) {
+ getProperties().put("concretePartOfPair","false");
+ }
+ super.setupContext();
+ }
+}
Index: tools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java
===================================================================
--- tools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java (revision 15916)
+++ tools/src/java/org/hibernate/tool/hbm2x/pojo/BasicPOJOClass.java (working copy)
@@ -38,7 +38,7 @@
*/
abstract public class BasicPOJOClass implements POJOClass, MetaAttributeConstants {
- protected ImportContext importContext;
+ protected ImportContextImpl importContext;
protected MetaAttributable meta;
protected final Cfg2JavaTool c2j;
@@ -83,7 +83,11 @@
/** Return package name. Note: Does not handle inner classes */
public String getPackageName() {
String generatedClass = getGeneratedClassName();
- return StringHelper.qualifier(generatedClass.trim());
+ String qualifier = StringHelper.qualifier(generatedClass.trim());
+ if ( importContext != null ) {
+ importContext.setBasePackage( qualifier ); // make sure that the import context stays in-sync with any overrides to our class name
+ }
+ return qualifier;
}
public String getShortName() {
Index: tools/src/java/org/hibernate/tool/hbm2x/pojo/ImportContextImpl.java
===================================================================
--- tools/src/java/org/hibernate/tool/hbm2x/pojo/ImportContextImpl.java (revision 15916)
+++ tools/src/java/org/hibernate/tool/hbm2x/pojo/ImportContextImpl.java (working copy)
@@ -148,4 +148,12 @@
}
return buf.toString();
}
+
+ public String getBasePackage() {
+ return basePackage;
+ }
+
+ public void setBasePackage( String basePackage ) {
+ this.basePackage = basePackage;
+ }
}
Index: tools/src/templates/pojo/Ejb3TypeDeclaration.ftl
===================================================================
--- tools/src/templates/pojo/Ejb3TypeDeclaration.ftl (revision 15916)
+++ tools/src/templates/pojo/Ejb3TypeDeclaration.ftl (working copy)
@@ -2,6 +2,9 @@
<#if pojo.isComponent()>
@${pojo.importType("javax.persistence.Embeddable")}
<#else>
+<#if basePartOfPair>
+@${pojo.importType("javax.persistence.MappedSuperclass")}
+<#else>
@${pojo.importType("javax.persistence.Entity")}
@${pojo.importType("javax.persistence.Table")}(name="${clazz.table.name}"
<#if clazz.table.schema?exists>
@@ -14,4 +17,5 @@
, uniqueConstraints = ${uniqueConstraint}
</#if>)
</#if>
+</#if>
</#if>
\ No newline at end of file
Index: tools/src/templates/pojo/PojoFields.ftl
===================================================================
--- tools/src/templates/pojo/PojoFields.ftl (revision 15916)
+++ tools/src/templates/pojo/PojoFields.ftl (working copy)
@@ -1,8 +1,9 @@
<#-- // Fields -->
-
+<#if (!concretePartOfPair)>
<#foreach field in pojo.getAllPropertiesIterator()><#if pojo.getMetaAttribAsBool(field, "gen-property", true)> <#if pojo.hasMetaAttribute(field, "field-description")> /**
${pojo.getFieldJavaDoc(field, 0)}
*/
- </#if> ${pojo.getFieldModifiers(field)} ${pojo.getJavaTypeName(field, jdk5)} ${field.name}<#if pojo.hasFieldInitializor(field, jdk5)> = ${pojo.getFieldInitialization(field, jdk5)}</#if>;
+ </#if> <#if basePartOfPair?if_exists>protected<#else>${pojo.getFieldModifiers(field)}</#if> ${pojo.getJavaTypeName(field, jdk5)} ${field.name}<#if pojo.hasFieldInitializor(field, jdk5)> = ${pojo.getFieldInitialization(field, jdk5)}</#if>;
</#if>
</#foreach>
+</#if>
Index: tools/src/templates/pojo/PojoPropertyAccessors.ftl
===================================================================
--- tools/src/templates/pojo/PojoPropertyAccessors.ftl (revision 15916)
+++ tools/src/templates/pojo/PojoPropertyAccessors.ftl (working copy)
@@ -1,4 +1,5 @@
<#-- // Property accessors -->
+<#if !concretePartOfPair>
<#foreach property in pojo.getAllPropertiesIterator()>
<#if pojo.getMetaAttribAsBool(property, "gen-property", true)>
<#if pojo.hasFieldJavaDoc(property)>
@@ -16,3 +17,4 @@
}
</#if>
</#foreach>
+</#if>
Index: tools/src/templates/pojo/PojoTypeDeclaration.ftl
===================================================================
--- tools/src/templates/pojo/PojoTypeDeclaration.ftl (revision 15916)
+++ tools/src/templates/pojo/PojoTypeDeclaration.ftl (working copy)
@@ -1,5 +1,7 @@
/**
-${pojo.getClassJavaDoc(pojo.getDeclarationName() + " generated by hbm2java", 0)}
+${pojo.getClassJavaDoc(pojo.getDeclarationName() + " generated by hbm2java", 0)}<#if basePartOfPair>
+ * Base part of a abstract/concrete pair.</#if><#if concretePartOfPair>
+ * Concrete part of a abstract/concrete pair.</#if>
*/
<#include "Ejb3TypeDeclaration.ftl"/>
${pojo.getClassModifiers()} ${pojo.getDeclarationType()} ${pojo.getDeclarationName()} ${pojo.getExtendsDeclaration()} ${pojo.getImplementsDeclaration()}