blob: bab7a2069f166eca3af4bdf5e34dcae414370a1d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2010 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.gen.internal.old;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Modifier;
import com.ibm.icu.text.Collator;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jpt.db.Column;
import org.eclipse.jpt.db.ForeignKey;
import org.eclipse.jpt.db.Table;
import org.eclipse.jpt.utility.JavaType;
import org.eclipse.jpt.utility.internal.BooleanReference;
import org.eclipse.jpt.utility.internal.IndentingPrintWriter;
import org.eclipse.jpt.utility.internal.NameTools;
import org.eclipse.jpt.utility.internal.StringTools;
import org.eclipse.jpt.utility.internal.iterators.FilteringIterator;
import org.eclipse.osgi.util.NLS;
// TODO format generated code per preferences
// TODO organize generated imports per preferences
/**
* This generator will generate an entity for a table.
*/
public class EntityGenerator {
final Config config;
private final IPackageFragment packageFragment;
private final GenTable genTable;
private final String entityClassName;
private final String pkClassName;
// ********** public API **********
static void generateEntity(
Config config,
IPackageFragment packageFragment,
GenTable genTable,
IProgressMonitor progressMonitor
) {
if ((config == null) || (packageFragment == null) || (genTable == null)) {
throw new NullPointerException();
}
new EntityGenerator(config, packageFragment, genTable).generateEntity(progressMonitor);
}
// ********** constructor/initialization **********
private EntityGenerator(Config config, IPackageFragment packageFragment, GenTable genTable) {
super();
this.config = config;
this.packageFragment = packageFragment;
this.genTable = genTable;
this.entityClassName = this.fullyQualify(this.getEntityName());
this.pkClassName = this.entityClassName + '.' + config.getPrimaryKeyMemberClassName();
}
// ********** code gen **********
private void generateEntity(IProgressMonitor progressMonitor) {
try {
this.generateEntity_(progressMonitor);
} catch (JavaModelException ex) {
throw new RuntimeException(ex);
}
}
private void generateEntity_(IProgressMonitor progressMonitor) throws JavaModelException {
SubMonitor sm = SubMonitor.convert(progressMonitor, this.buildTaskName(), 100);
String fileName = this.getEntityName() + ".java"; //$NON-NLS-1$
String source = this.buildSource();
sm.worked(20);
try {
this.packageFragment.createCompilationUnit(fileName, source, false, sm.newChild(40));
} catch (JavaModelException ex) {
if (ex.getJavaModelStatus().getCode() == IJavaModelStatusConstants.NAME_COLLISION) {
if (this.config.getOverwriteConfirmer().overwrite(this.entityClassName)) {
this.packageFragment.createCompilationUnit(fileName, source, true, sm.newChild(40));
}
} else {
throw ex;
}
}
sm.setWorkRemaining(0);
}
private String buildTaskName() {
return NLS.bind(JptGenMessages.EntityGenerator_taskName, this.getEntityName());
}
/**
* build the "body" source first; then build the "package" and "imports" source
* and concatenate the "body" source to it
*/
private String buildSource() {
// build the body source first so we can gather up the import statements
BodySource bodySource = this.buildBodySource();
StringWriter sw = new StringWriter(bodySource.length() + 2000);
PrintWriter pw = new PrintWriter(sw);
this.printPackageAndImportsOn(pw, bodySource);
pw.print(bodySource.getSource());
return sw.toString();
}
private BodySource buildBodySource() {
EntitySourceWriter pw = new EntitySourceWriter(this.getPackageName(), this.entityClassName);
this.printBodySourceOn(pw);
return pw;
}
private void printBodySourceOn(EntitySourceWriter pw) {
this.printClassDeclarationOn(pw);
pw.indent();
this.printEntityPrimaryKeyFieldsOn(pw);
this.printEntityNonPrimaryKeyBasicFieldsOn(pw);
this.printEntityManyToOneFieldsOn(pw);
this.printEntityOneToManyFieldsOn(pw);
this.printEntityOwnedManyToManyFieldsOn(pw);
this.printEntityNonOwnedManyToManyFieldsOn(pw);
this.printSerialVersionUIDFieldOn(pw);
pw.println();
this.printZeroArgumentConstructorOn(this.getEntityName(), this.config.getMethodVisibilityClause(), pw);
if (this.config.propertyAccessType() || this.config.generateGettersAndSetters()) {
this.printEntityPrimaryKeyPropertiesOn(pw);
this.printEntityNonPrimaryKeyBasicPropertiesOn(pw);
this.printEntityManyToOnePropertiesOn(pw);
this.printEntityOneToManyPropertiesOn(pw);
this.printEntityOwnedManyToManyPropertiesOn(pw);
this.printEntityNonOwnedManyToManyPropertiesOn(pw);
}
if (this.primaryKeyClassIsRequired()) {
this.printPrimaryKeyClassOn(pw);
}
pw.undent();
pw.print('}');
pw.println(); // EOF
}
// ********** class declaration **********
private void printClassDeclarationOn(EntitySourceWriter pw) {
this.printEntityAnnotationOn(pw);
this.printTableAnnotationOn(pw);
this.printIdClassAnnotationOn(pw);
pw.print("public class "); //$NON-NLS-1$
pw.printTypeDeclaration(this.entityClassName);
if (config.serializable()) {
pw.print(" implements "); //$NON-NLS-1$
pw.printTypeDeclaration(Serializable.class.getName());
}
pw.print(" {"); //$NON-NLS-1$
pw.println();
}
private void printEntityAnnotationOn(EntitySourceWriter pw) {
pw.printAnnotation(JPA.ENTITY);
pw.println();
}
private void printTableAnnotationOn(EntitySourceWriter pw) {
String tableName = this.config.getDatabaseAnnotationNameBuilder().buildTableAnnotationName(this.getEntityName(), this.getTable());
if (tableName == null) {
return; // the default table name is OK
}
pw.printAnnotation(JPA.TABLE);
pw.print("(name="); //$NON-NLS-1$
pw.printStringLiteral(tableName);
pw.print(')');
pw.println();
}
private void printIdClassAnnotationOn(EntitySourceWriter pw) {
if (this.primaryKeyClassIsRequired() && this.config.generateIdClassForCompoundPK()) {
pw.printAnnotation(JPA.ID_CLASS);
pw.print('(');
pw.printTypeDeclaration(this.pkClassName);
pw.print(".class)"); //$NON-NLS-1$
pw.println();
}
}
// ********** primary key fields **********
private void printEntityPrimaryKeyFieldsOn(EntitySourceWriter pw) {
if (this.primaryKeyClassIsRequired() && this.config.generateEmbeddedIdForCompoundPK()) {
this.printEntityEmbeddedIdPrimaryKeyFieldOn(pw);
} else {
this.printEntityReadOnlyPrimaryKeyFieldsOn(pw);
this.printEntityWritablePrimaryKeyFieldsOn(pw);
}
}
private void printEntityEmbeddedIdPrimaryKeyFieldOn(EntitySourceWriter pw) {
if (this.config.fieldAccessType()) {
pw.printAnnotation(JPA.EMBEDDED_ID);
pw.println();
}
this.printFieldOn(this.genTable.getAttributeNameForEmbeddedId(), this.pkClassName, pw);
}
private void printEntityReadOnlyPrimaryKeyFieldsOn(EntitySourceWriter pw) {
this.printPrimaryKeyFieldsOn(pw, true, true); // true=read-only; true=print ID annotation on fields
}
private void printEntityWritablePrimaryKeyFieldsOn(EntitySourceWriter pw) {
this.printPrimaryKeyFieldsOn(pw, false, true); // false=writable; true=print ID annotation on fields
}
private void printPrimaryKeyFieldsOn(EntitySourceWriter pw, boolean readOnly, boolean printIdAnnotation) {
for (Column column : this.getPrimaryKeyColumns(readOnly)) {
this.printPrimaryKeyFieldOn(column, pw, readOnly, printIdAnnotation);
}
}
private Iterable<Column> getPrimaryKeyColumns(boolean readOnly) {
return readOnly ? this.genTable.getReadOnlyPrimaryKeyColumns() : this.genTable.getWritablePrimaryKeyColumns();
}
// TODO if the field's type is java.util/sql.Date, it needs @Temporal(DATE)
// TODO if the primary key is auto-generated, the field must be an integral type
private void printPrimaryKeyFieldOn(Column column, EntitySourceWriter pw, boolean readOnly, boolean printIdAnnotation) {
String fieldName = this.genTable.getAttributeNameFor(column);
if (this.config.fieldAccessType()) {
if (printIdAnnotation) {
pw.printAnnotation(JPA.ID);
pw.println();
}
String columnName = this.config.getDatabaseAnnotationNameBuilder().buildColumnAnnotationName(fieldName, column);
if (readOnly) {
this.printReadOnlyColumnAnnotationOn(columnName, pw);
} else {
this.printColumnAnnotationOn(columnName, pw);
}
}
this.printFieldOn(fieldName, column.getPrimaryKeyJavaTypeDeclaration(), pw);
}
private void printReadOnlyColumnAnnotationOn(String columnName, EntitySourceWriter pw) {
pw.printAnnotation(JPA.COLUMN);
pw.print('(');
if (columnName != null) {
pw.print("name="); //$NON-NLS-1$
pw.printStringLiteral(columnName);
pw.print(", "); //$NON-NLS-1$
}
pw.print("insertable=false, updatable=false)"); //$NON-NLS-1$
pw.println();
}
// ********** basic fields **********
private void printEntityNonPrimaryKeyBasicFieldsOn(EntitySourceWriter pw) {
for (Column column : this.genTable.getNonPrimaryKeyBasicColumns()) {
this.printEntityNonPrimaryKeyBasicFieldOn(column, pw);
}
}
private void printEntityNonPrimaryKeyBasicFieldOn(Column column, EntitySourceWriter pw) {
String fieldName = this.genTable.getAttributeNameFor(column);
if (this.config.fieldAccessType()) {
String columnName = this.config.getDatabaseAnnotationNameBuilder().buildColumnAnnotationName(fieldName, column);
this.printColumnAnnotationOn(columnName, pw);
}
if (column.isLOB()) {
pw.printAnnotation(JPA.LOB);
pw.println();
}
this.printFieldOn(fieldName, column.getJavaTypeDeclaration(), pw);
}
private void printColumnAnnotationOn(String columnName, EntitySourceWriter pw) {
if (columnName != null) { // the column name is null if the default is OK
pw.printAnnotation(JPA.COLUMN);
pw.print("(name="); //$NON-NLS-1$
pw.printStringLiteral(columnName);
pw.print(')');
pw.println();
}
}
// ********** many-to-one fields **********
private void printEntityManyToOneFieldsOn(EntitySourceWriter pw) {
for (Iterator<ManyToOneRelation> stream = this.genTable.manyToOneRelations(); stream.hasNext(); ) {
this.printEntityManyToOneFieldOn(stream.next(), pw);
}
}
private void printEntityManyToOneFieldOn(ManyToOneRelation relation, EntitySourceWriter pw) {
String fieldName = this.genTable.getAttributeNameFor(relation);
if (this.config.fieldAccessType()) {
this.printManyToOneAnnotationOn(fieldName, relation, pw);
}
this.printFieldOn(fieldName, this.fullyQualify(relation.getReferencedEntityName()), pw);
}
private void printManyToOneAnnotationOn(String attributeName, ManyToOneRelation relation, EntitySourceWriter pw) {
pw.printAnnotation(JPA.MANY_TO_ONE);
pw.println();
ForeignKey foreignKey = relation.getForeignKey();
if (foreignKey.referencesSingleColumnPrimaryKey()) {
// if the FK references a single-column PK, 'referencedColumnName' is not required
String joinColumnName = this.config.getDatabaseAnnotationNameBuilder().buildJoinColumnAnnotationName(attributeName, foreignKey);
if (joinColumnName == null) {
// no JoinColumn annotation needed: the default 'name' and 'referencedColumnName' work
} else {
// there is only a single join column here (just not the default name)
this.printJoinColumnAnnotationOn(joinColumnName, null, pw);
pw.println();
}
} else {
this.printManyToOneJoinColumnsAnnotationOn(foreignKey, pw);
}
}
private void printManyToOneJoinColumnsAnnotationOn(ForeignKey foreignKey, EntitySourceWriter pw) {
if (foreignKey.getColumnPairsSize() > 1) {
pw.printAnnotation(JPA.JOIN_COLUMNS);
pw.print("({"); //$NON-NLS-1$
pw.println();
pw.indent();
}
this.printJoinColumnAnnotationsOn(foreignKey, pw);
if (foreignKey.getColumnPairsSize() > 1) {
pw.undent();
pw.println();
pw.print("})"); //$NON-NLS-1$
}
pw.println();
}
private void printJoinColumnAnnotationsOn(ForeignKey foreignKey, EntitySourceWriter pw) {
for (Iterator<ForeignKey.ColumnPair> stream = foreignKey.getColumnPairs().iterator(); stream.hasNext(); ) {
this.printJoinColumnAnnotationOn(stream.next(), pw);
if (stream.hasNext()) {
pw.println(',');
}
}
}
private void printJoinColumnAnnotationOn(ForeignKey.ColumnPair columnPair, EntitySourceWriter pw) {
this.printJoinColumnAnnotationOn(
this.config.getDatabaseAnnotationNameBuilder().buildJoinColumnAnnotationName(columnPair.getBaseColumn()),
this.config.getDatabaseAnnotationNameBuilder().buildJoinColumnAnnotationName(columnPair.getReferencedColumn()),
pw
);
}
/**
* 'baseColumnName' cannot be null;
* 'referencedColumnName' is null when the default is applicable (i.e. the
* referenced column is the single-column primary key column of the
* referenced table)
*/
private void printJoinColumnAnnotationOn(String baseColumnName, String referencedColumnName, EntitySourceWriter pw) {
pw.printAnnotation(JPA.JOIN_COLUMN);
pw.print("(name="); //$NON-NLS-1$
pw.printStringLiteral(baseColumnName);
if (referencedColumnName != null) {
pw.print(", referencedColumnName="); //$NON-NLS-1$
pw.printStringLiteral(referencedColumnName);
}
pw.print(')');
}
// ********** one-to-many fields **********
private void printEntityOneToManyFieldsOn(EntitySourceWriter pw) {
for (Iterator<OneToManyRelation> stream = this.genTable.oneToManyRelations(); stream.hasNext(); ) {
this.printEntityOneToManyFieldOn(stream.next(), pw);
}
}
private void printEntityOneToManyFieldOn(OneToManyRelation relation, EntitySourceWriter pw) {
String fieldName = this.genTable.getAttributeNameFor(relation);
if (this.config.fieldAccessType()) {
this.printOneToManyAnnotationOn(relation, pw);
}
this.printCollectionFieldOn(fieldName, this.fullyQualify(relation.getReferencedEntityName()), pw);
}
private void printOneToManyAnnotationOn(OneToManyRelation relation, EntitySourceWriter pw) {
pw.printAnnotation(JPA.ONE_TO_MANY);
pw.print("(mappedBy=\""); //$NON-NLS-1$
pw.print(relation.getMappedBy());
pw.print("\")"); //$NON-NLS-1$
pw.println();
}
// ********** owned many-to-many fields **********
private void printEntityOwnedManyToManyFieldsOn(EntitySourceWriter pw) {
for (Iterator<ManyToManyRelation> stream = this.genTable.ownedManyToManyRelations(); stream.hasNext(); ) {
this.printEntityOwnedManyToManyFieldOn(stream.next(), pw);
}
}
private void printEntityOwnedManyToManyFieldOn(ManyToManyRelation relation, EntitySourceWriter pw) {
String fieldName = this.genTable.getAttributeNameForOwned(relation);
if (this.config.fieldAccessType()) {
this.printOwnedManyToManyAnnotationOn(fieldName, relation, pw);
}
this.printCollectionFieldOn(fieldName, this.fullyQualify(relation.getNonOwningEntityName()), pw);
}
/**
* only print the JoinTable annotation if one or more of the
* [generated] elements is not defaulted:
* name
* joinColumns
* inverseJoinColumns
* thus the need for the 'printJoinTableAnnotation' flag
*/
private void printOwnedManyToManyAnnotationOn(String attributeName, ManyToManyRelation relation, EntitySourceWriter pw) {
pw.printAnnotation(JPA.MANY_TO_MANY);
pw.println();
BooleanReference printJoinTableAnnotation = new BooleanReference(true);
if ( ! relation.joinTableNameIsDefault()) { // db-only test - no need to delegate to platform?
printJoinTableAnnotation.setFalse();
pw.printAnnotation(JPA.JOIN_TABLE);
pw.print("(name="); //$NON-NLS-1$
pw.printStringLiteral(this.config.getDatabaseAnnotationNameBuilder().buildJoinTableAnnotationName(relation.getJoinGenTable().getTable()));
}
this.printJoinTableJoinColumnAnnotationsOn(
"joinColumns", //$NON-NLS-1$
attributeName,
relation.getOwningForeignKey(),
printJoinTableAnnotation,
pw
);
this.printJoinTableJoinColumnAnnotationsOn(
"inverseJoinColumns", //$NON-NLS-1$
relation.getNonOwningGenTable().getAttributeNameForNonOwned(relation),
relation.getNonOwningForeignKey(),
printJoinTableAnnotation,
pw
);
if (printJoinTableAnnotation.isFalse()) {
pw.print(')');
pw.println();
}
}
/**
* 'elementName' is either "joinColumns" or "inverseJoinColumns"
*/
private void printJoinTableJoinColumnAnnotationsOn(String elementName, String attributeName, ForeignKey foreignKey, BooleanReference printJoinTableAnnotation, EntitySourceWriter pw) {
// we have to pre-calculate whether either 'name' and/or 'referencedColumnName'
// is required because they are wrapped by the JoinTable annotation and we
// need to print the JoinTable annotation first (if it hasn't already been printed)
boolean printRef = ! foreignKey.referencesSingleColumnPrimaryKey();
// if 'referencedColumnName' is required, 'name' is also required (i.e. it cannot be defaulted);
// but we will calculate it later [1], since there could be multiple join columns
String joinColumnName = (printRef) ?
null // 'joinColumnName' is not used
:
this.config.getDatabaseAnnotationNameBuilder().buildJoinColumnAnnotationName(attributeName, foreignKey);
boolean printBase = (printRef || (joinColumnName != null));
if (printBase || printRef) {
if (printJoinTableAnnotation.isTrue()) {
printJoinTableAnnotation.setFalse();
pw.printAnnotation(JPA.JOIN_TABLE);
pw.print('(');
} else {
pw.print(',');
}
pw.println();
pw.indent();
if (printRef) {
// if 'printRef' is true, 'joinColumnName' will always be "IGNORED" (so we ignore it)
this.printJoinTableJoinColumnAnnotationsOn(elementName, foreignKey, pw); // [1]
} else {
// if the FK references a single-column PK, 'referencedColumnName' is not required
if (printBase) {
// there is only a single join column here (just not the default name)
pw.print(elementName);
pw.print('=');
this.printJoinColumnAnnotationOn(joinColumnName, null, pw);
} else {
// no JoinColumn annotation needed: the default 'name' and 'referencedColumnName' work
}
}
pw.undent();
}
}
/**
* 'elementName' is either "joinColumns" or "inverseJoinColumns"
*/
private void printJoinTableJoinColumnAnnotationsOn(String elementName, ForeignKey foreignKey, EntitySourceWriter pw) {
pw.print(elementName);
pw.print('=');
if (foreignKey.getColumnPairsSize() > 1) {
pw.print('{');
pw.println();
pw.indent();
}
this.printJoinColumnAnnotationsOn(foreignKey, pw);
if (foreignKey.getColumnPairsSize() > 1) {
pw.undent();
pw.println();
pw.print('}');
pw.println();
}
}
// ********** non-owned many-to-many fields **********
private void printEntityNonOwnedManyToManyFieldsOn(EntitySourceWriter pw) {
for (Iterator<ManyToManyRelation> stream = this.genTable.nonOwnedManyToManyRelations(); stream.hasNext(); ) {
this.printEntityNonOwnedManyToManyFieldOn(stream.next(), pw);
}
}
private void printEntityNonOwnedManyToManyFieldOn(ManyToManyRelation relation, EntitySourceWriter pw) {
String fieldName = this.genTable.getAttributeNameForNonOwned(relation);
if (this.config.fieldAccessType()) {
this.printNonOwnedManyToManyAnnotationOn(relation, pw);
}
this.printCollectionFieldOn(fieldName, this.fullyQualify(relation.getOwningEntityName()), pw);
}
private void printNonOwnedManyToManyAnnotationOn(ManyToManyRelation relation, EntitySourceWriter pw) {
pw.printAnnotation(JPA.MANY_TO_MANY);
pw.print("(mappedBy=\""); //$NON-NLS-1$
pw.print(relation.getMappedBy());
pw.print("\")"); //$NON-NLS-1$
pw.println();
}
// ********** misc **********
private void printSerialVersionUIDFieldOn(EntitySourceWriter pw) {
if (this.config.generateSerialVersionUID()) {
pw.print("private static final long serialVersionUID = 1L;"); //$NON-NLS-1$
pw.println();
}
}
private void printZeroArgumentConstructorOn(String ctorName, String visibility, EntitySourceWriter pw) {
if (this.config.generateDefaultConstructor()) {
pw.printVisibility(visibility);
pw.print(ctorName);
pw.print("() {"); //$NON-NLS-1$
pw.println();
pw.indent();
pw.println("super();"); //$NON-NLS-1$
pw.undent();
pw.print('}');
pw.println();
pw.println();
}
}
// ********** primary key properties **********
private void printEntityPrimaryKeyPropertiesOn(EntitySourceWriter pw) {
if (this.primaryKeyClassIsRequired() && this.config.generateEmbeddedIdForCompoundPK()) {
this.printEntityEmbeddedIdPrimaryKeyPropertyOn(pw);
} else {
this.printEntityReadOnlyPrimaryKeyPropertiesOn(pw);
this.printEntityWritablePrimaryKeyPropertiesOn(pw);
}
}
private void printEntityEmbeddedIdPrimaryKeyPropertyOn(EntitySourceWriter pw) {
if (this.config.propertyAccessType()) {
pw.printAnnotation(JPA.EMBEDDED_ID);
pw.println();
}
this.printPropertyOn(this.genTable.getAttributeNameForEmbeddedId(), this.pkClassName, pw);
}
private void printEntityReadOnlyPrimaryKeyPropertiesOn(EntitySourceWriter pw) {
this.printPrimaryKeyPropertiesOn(pw, true, true); // true=read-only; true=print ID annotation on getters
}
private void printEntityWritablePrimaryKeyPropertiesOn(EntitySourceWriter pw) {
this.printPrimaryKeyPropertiesOn(pw, false, true); // false=writable; true=print ID annotation on getters
}
private void printPrimaryKeyPropertiesOn(EntitySourceWriter pw, boolean readOnly, boolean printIdAnnotation) {
for (Column column : this.getPrimaryKeyColumns(readOnly)) {
this.printPrimaryKeyPropertyOn(column, pw, readOnly, printIdAnnotation);
}
}
// TODO if the property's type is java.util/sql.Date, it needs @Temporal(DATE)
// TODO if the primary key is auto-generated, the property must be an integral type
private void printPrimaryKeyPropertyOn(Column column, EntitySourceWriter pw, boolean readOnly, boolean printIdAnnotation) {
String propertyName = this.genTable.getAttributeNameFor(column);
if (this.config.propertyAccessType()) {
if (printIdAnnotation) {
pw.printAnnotation(JPA.ID);
pw.println();
}
String columnName = this.config.getDatabaseAnnotationNameBuilder().buildColumnAnnotationName(propertyName, column);
if (readOnly) {
this.printReadOnlyColumnAnnotationOn(columnName, pw);
} else {
this.printColumnAnnotationOn(columnName, pw);
}
}
this.printPropertyOn(propertyName, column.getPrimaryKeyJavaTypeDeclaration(), pw);
}
// ********** basic properties **********
private void printEntityNonPrimaryKeyBasicPropertiesOn(EntitySourceWriter pw) {
for (Column column : this.genTable.getNonPrimaryKeyBasicColumns()) {
this.printEntityNonPrimaryKeyBasicPropertyOn(column, pw);
}
}
private void printEntityNonPrimaryKeyBasicPropertyOn(Column column, EntitySourceWriter pw) {
String propertyName = this.genTable.getAttributeNameFor(column);
if (this.config.propertyAccessType()) {
String columnName = this.config.getDatabaseAnnotationNameBuilder().buildColumnAnnotationName(propertyName, column);
this.printColumnAnnotationOn(columnName, pw);
}
this.printPropertyOn(propertyName, column.getJavaTypeDeclaration(), pw);
}
// ********** many-to-one properties **********
private void printEntityManyToOnePropertiesOn(EntitySourceWriter pw) {
for (Iterator<ManyToOneRelation> stream = this.genTable.manyToOneRelations(); stream.hasNext(); ) {
this.printEntityManyToOnePropertyOn(stream.next(), pw);
}
}
private void printEntityManyToOnePropertyOn(ManyToOneRelation relation, EntitySourceWriter pw) {
String propertyName = this.genTable.getAttributeNameFor(relation);
if (this.config.propertyAccessType()) {
this.printManyToOneAnnotationOn(propertyName, relation, pw);
}
String typeDeclaration = this.fullyQualify(relation.getReferencedEntityName());
this.printPropertyOn(propertyName, typeDeclaration, pw);
}
// ********** one-to-many properties **********
private void printEntityOneToManyPropertiesOn(EntitySourceWriter pw) {
for (Iterator<OneToManyRelation> stream = this.genTable.oneToManyRelations(); stream.hasNext(); ) {
this.printEntityOneToManyPropertyOn(stream.next(), pw);
}
}
private void printEntityOneToManyPropertyOn(OneToManyRelation relation, EntitySourceWriter pw) {
String propertyName = this.genTable.getAttributeNameFor(relation);
if (this.config.propertyAccessType()) {
this.printOneToManyAnnotationOn(relation, pw);
}
String elementTypeDeclaration = this.fullyQualify(relation.getReferencedEntityName());
this.printCollectionPropertyOn(propertyName, elementTypeDeclaration, pw);
}
// ********** owned many-to-many properties **********
private void printEntityOwnedManyToManyPropertiesOn(EntitySourceWriter pw) {
for (Iterator<ManyToManyRelation> stream = this.genTable.ownedManyToManyRelations(); stream.hasNext(); ) {
this.printEntityOwnedManyToManyPropertyOn(stream.next(), pw);
}
}
private void printEntityOwnedManyToManyPropertyOn(ManyToManyRelation relation, EntitySourceWriter pw) {
String propertyName = this.genTable.getAttributeNameForOwned(relation);
if (this.config.propertyAccessType()) {
this.printOwnedManyToManyAnnotationOn(propertyName, relation, pw);
}
String elementTypeDeclaration = this.fullyQualify(relation.getNonOwningEntityName());
this.printCollectionPropertyOn(propertyName, elementTypeDeclaration, pw);
}
// ********** non-owned many-to-many properties **********
private void printEntityNonOwnedManyToManyPropertiesOn(EntitySourceWriter pw) {
for (Iterator<ManyToManyRelation> stream = this.genTable.nonOwnedManyToManyRelations(); stream.hasNext(); ) {
this.printEntityNonOwnedManyToManyPropertyOn(stream.next(), pw);
}
}
private void printEntityNonOwnedManyToManyPropertyOn(ManyToManyRelation relation, EntitySourceWriter pw) {
String propertyName = this.genTable.getAttributeNameForNonOwned(relation);
if (this.config.propertyAccessType()) {
this.printNonOwnedManyToManyAnnotationOn(relation, pw);
}
String elementTypeDeclaration = this.fullyQualify(relation.getOwningEntityName());
this.printCollectionPropertyOn(propertyName, elementTypeDeclaration, pw);
}
// ********** compound primary key class **********
private void printPrimaryKeyClassOn(EntitySourceWriter pw) {
pw.println();
if (this.config.generateEmbeddedIdForCompoundPK()) {
pw.printAnnotation(JPA.EMBEDDABLE);
pw.println();
}
pw.print("public static class "); //$NON-NLS-1$
pw.print(this.config.getPrimaryKeyMemberClassName());
pw.print(" implements "); //$NON-NLS-1$
pw.printTypeDeclaration(Serializable.class.getName());
pw.print(" {"); //$NON-NLS-1$
pw.println();
pw.indent();
if (this.config.generateEmbeddedIdForCompoundPK()) {
this.printEmbeddableReadOnlyPrimaryKeyFieldsOn(pw);
this.printEmbeddableWritablePrimaryKeyFieldsOn(pw);
} else {
this.printIdFieldsOn(pw);
}
this.printSerialVersionUIDFieldOn(pw);
pw.println();
this.printZeroArgumentConstructorOn(this.config.getPrimaryKeyMemberClassName(), "public", pw); //$NON-NLS-1$
if (this.config.propertyAccessType() || this.config.generateGettersAndSetters()) {
if (this.config.generateEmbeddedIdForCompoundPK()) {
this.printEmbeddableReadOnlyPrimaryKeyPropertiesOn(pw);
this.printEmbeddableWritablePrimaryKeyPropertiesOn(pw);
} else {
this.printIdPropertiesOn(pw);
}
}
this.printPrimaryKeyEqualsMethodOn(this.config.getPrimaryKeyMemberClassName(), this.getTable().getPrimaryKeyColumns(), pw);
this.printPrimaryKeyHashCodeMethodOn(this.getTable().getPrimaryKeyColumns(), pw);
pw.undent();
pw.print('}');
pw.println();
pw.println();
}
// ********** compound primary key class fields **********
private void printEmbeddableReadOnlyPrimaryKeyFieldsOn(EntitySourceWriter pw) {
this.printPrimaryKeyFieldsOn(pw, true, false); // true=read-only; false=do not print ID annotation on fields
}
private void printEmbeddableWritablePrimaryKeyFieldsOn(EntitySourceWriter pw) {
this.printPrimaryKeyFieldsOn(pw, false, false); // false=writable; false=do not print ID annotation on fields
}
private void printIdFieldsOn(EntitySourceWriter pw) {
for (Column column : this.getTable().getPrimaryKeyColumns()) {
this.printIdFieldOn(column, pw);
}
}
private void printIdFieldOn(Column column, EntitySourceWriter pw) {
this.printFieldOn(this.genTable.getAttributeNameFor(column), column.getPrimaryKeyJavaTypeDeclaration(), pw);
}
// ********** compound primary key class properties **********
private void printEmbeddableReadOnlyPrimaryKeyPropertiesOn(EntitySourceWriter pw) {
this.printPrimaryKeyPropertiesOn(pw, true, false); // true=read-only; false=do not print ID annotation on getters
}
private void printEmbeddableWritablePrimaryKeyPropertiesOn(EntitySourceWriter pw) {
this.printPrimaryKeyPropertiesOn(pw, false, false); // false=writable; false=do not print ID annotation on getters
}
private void printIdPropertiesOn(EntitySourceWriter pw) {
for (Column column : this.getTable().getPrimaryKeyColumns()) {
this.printIdPropertyOn(column, pw);
}
}
private void printIdPropertyOn(Column column, EntitySourceWriter pw) {
this.printPropertyOn(this.genTable.getAttributeNameFor(column), column.getPrimaryKeyJavaTypeDeclaration(), pw);
}
// ********** compound primary key class equals **********
private void printPrimaryKeyEqualsMethodOn(String className, Iterable<Column> columns, EntitySourceWriter pw) {
pw.printAnnotation("java.lang.Override"); //$NON-NLS-1$
pw.println();
pw.println("public boolean equals(Object o) {"); //$NON-NLS-1$
pw.indent();
pw.println("if (o == this) {"); //$NON-NLS-1$
pw.indent();
pw.println("return true;"); //$NON-NLS-1$
pw.undent();
pw.print('}');
pw.println();
pw.print("if ( ! (o instanceof "); //$NON-NLS-1$
pw.print(className);
pw.print(")) {"); //$NON-NLS-1$
pw.println();
pw.indent();
pw.println("return false;"); //$NON-NLS-1$
pw.undent();
pw.print('}');
pw.println();
pw.print(className);
pw.print(" other = ("); //$NON-NLS-1$
pw.print(className);
pw.print(") o;"); //$NON-NLS-1$
pw.println();
pw.print("return "); //$NON-NLS-1$
pw.indent();
for (Iterator<Column> stream = columns.iterator(); stream.hasNext(); ) {
this.printPrimaryKeyEqualsClauseOn(stream.next(), pw);
if (stream.hasNext()) {
pw.println();
pw.print("&& "); //$NON-NLS-1$
}
}
pw.print(';');
pw.println();
pw.undent();
pw.undent();
pw.print('}');
pw.println();
pw.println();
}
private void printPrimaryKeyEqualsClauseOn(Column column, EntitySourceWriter pw) {
String fieldName = this.genTable.getAttributeNameFor(column);
JavaType javaType = column.getPrimaryKeyJavaType();
if (javaType.isPrimitive()) {
this.printPrimitiveEqualsClauseOn(fieldName, pw);
} else {
this.printReferenceEqualsClauseOn(fieldName, pw);
}
}
private void printPrimitiveEqualsClauseOn(String fieldName, EntitySourceWriter pw) {
pw.print("(this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(" == other."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(')');
}
private void printReferenceEqualsClauseOn(String fieldName, EntitySourceWriter pw) {
pw.print("this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(".equals(other."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(')');
}
// ********** compound primary key class hash code **********
private void printPrimaryKeyHashCodeMethodOn(Iterable<Column> columns, EntitySourceWriter pw) {
pw.printAnnotation("java.lang.Override"); //$NON-NLS-1$
pw.println();
pw.println("public int hashCode() {"); //$NON-NLS-1$
pw.indent();
pw.println("final int prime = 31;"); //$NON-NLS-1$
pw.println("int hash = 17;"); //$NON-NLS-1$
for (Column column : columns) {
pw.print("hash = hash * prime + "); //$NON-NLS-1$
this.printPrimaryKeyHashCodeClauseOn(column, pw);
pw.print(';');
pw.println();
}
pw.println("return hash;"); //$NON-NLS-1$
pw.undent();
pw.print('}');
pw.println();
pw.println();
}
private void printPrimaryKeyHashCodeClauseOn(Column column, EntitySourceWriter pw) {
String fieldName = this.genTable.getAttributeNameFor(column);
JavaType javaType = column.getPrimaryKeyJavaType();
if (javaType.isPrimitive()) {
this.printPrimitiveHashCodeClauseOn(javaType.getElementTypeName(), fieldName, pw);
} else {
this.printReferenceHashCodeClauseOn(fieldName, pw);
}
}
private void printPrimitiveHashCodeClauseOn(String primitiveName, String fieldName, EntitySourceWriter pw) {
if (primitiveName.equals("int")) { //$NON-NLS-1$
// this.value
pw.print("this."); //$NON-NLS-1$
pw.print(fieldName);
} else if (primitiveName.equals("short") //$NON-NLS-1$
|| primitiveName.equals("byte") //$NON-NLS-1$
|| primitiveName.equals("char")) { //$NON-NLS-1$
// ((int) this.value) - explicit cast
pw.print("((int) this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(')');
} else if (primitiveName.equals("long")) { // cribbed from Long#hashCode() //$NON-NLS-1$
// ((int) (this.value ^ (this.value >>> 32)))
pw.print("((int) (this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(" ^ (this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(" >>> 32)))"); //$NON-NLS-1$
} else if (primitiveName.equals("float")) { // cribbed from Float#hashCode() //$NON-NLS-1$
// java.lang.Float.floatToIntBits(this.value)
pw.printTypeDeclaration("java.lang.Float"); //$NON-NLS-1$
pw.print(".floatToIntBits(this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(')');
} else if (primitiveName.equals("double")) { // cribbed from Double#hashCode() //$NON-NLS-1$
// ((int) (java.lang.Double.doubleToLongBits(this.value) ^ (java.lang.Double.doubleToLongBits(this.value) >>> 32)))
pw.print("((int) ("); //$NON-NLS-1$
pw.printTypeDeclaration("java.lang.Double"); //$NON-NLS-1$
pw.print(".doubleToLongBits(this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(") ^ ("); //$NON-NLS-1$
pw.printTypeDeclaration("java.lang.Double"); //$NON-NLS-1$
pw.print(".doubleToLongBits(this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(") >>> 32)))"); //$NON-NLS-1$
} else if (primitiveName.equals("boolean")) { //$NON-NLS-1$
// (this.value ? 1 : 0)
pw.print("(this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(" ? 1 : 0)"); //$NON-NLS-1$
} else {
throw new IllegalArgumentException(primitiveName);
}
}
private void printReferenceHashCodeClauseOn(String fieldName, EntitySourceWriter pw) {
pw.print("this."); //$NON-NLS-1$
pw.print(fieldName);
pw.print(".hashCode()"); //$NON-NLS-1$
}
// ********** package and imports **********
private void printPackageAndImportsOn(PrintWriter pw, BodySource bodySource) {
if (this.getPackageName().length() != 0) {
pw.print("package "); //$NON-NLS-1$
pw.print(this.getPackageName());
pw.print(';');
pw.println();
pw.println();
}
for (Iterator<Map.Entry<String, String>> stream = bodySource.importEntries(); stream.hasNext(); ) {
Map.Entry<String, String> entry = stream.next();
pw.print("import "); //$NON-NLS-1$
pw.print(entry.getValue()); // package
pw.print('.');
pw.print(entry.getKey()); // short class name
pw.print(';');
pw.println();
}
pw.println();
}
// ********** fields **********
/**
* visibility is set in the config
*/
private void printFieldOn(String fieldName, String typeDeclaration, EntitySourceWriter pw) {
pw.printField(
fieldName,
typeDeclaration,
this.config.getFieldVisibilityClause()
);
}
/**
* visibility and collection type are set in the config
*/
private void printCollectionFieldOn(String fieldName, String elementTypeDeclaration, EntitySourceWriter pw) {
pw.printParameterizedField(
fieldName,
this.config.getCollectionTypeName(),
elementTypeDeclaration,
this.config.getFieldVisibilityClause()
);
}
// ********** properties **********
/**
* visibility is set in the config
*/
private void printPropertyOn(String propertyName, String typeDeclaration, EntitySourceWriter pw) {
pw.printGetterAndSetter(
propertyName,
typeDeclaration,
this.config.getMethodVisibilityClause()
);
}
/**
* visibility and collection type are set in the config
*/
private void printCollectionPropertyOn(String propertyName, String elementTypeDeclaration, EntitySourceWriter pw) {
pw.printCollectionGetterAndSetter(
propertyName,
this.config.getCollectionTypeName(),
elementTypeDeclaration,
this.config.getMethodVisibilityClause()
);
}
// ********** convenience methods **********
private String getPackageName() {
return this.packageFragment.getElementName();
}
private Table getTable() {
return this.genTable.getTable();
}
private String getEntityName() {
return this.genTable.getEntityName();
}
private boolean primaryKeyClassIsRequired() {
return this.getTable().getPrimaryKeyColumnsSize() > 1;
}
private String fullyQualify(String shortClassName) {
String pkg = this.getPackageName();
return (pkg.length() == 0) ? shortClassName : pkg + '.' + shortClassName;
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.genTable.getName() + " => " + this.entityClassName); //$NON-NLS-1$
}
// ********** source writer **********
private interface BodySource {
/**
* return a sorted set of map entries; the key is the short class name,
* the value is the package name
*/
Iterator<Map.Entry<String, String>> importEntries();
/**
* return the body source code
*/
String getSource();
/**
* return the length of the body source code
*/
int length();
}
/**
* Extend IndentingPrintWriter with some methods that facilitate building
* class source code.
*/
private static class EntitySourceWriter extends IndentingPrintWriter implements BodySource {
final String packageName;
final String className;
// key = short class name; value = package name
private final Map<String, String> imports = new HashMap<String, String>();
EntitySourceWriter(String packageName, String className) {
super(new StringWriter(20000));
this.packageName = packageName;
this.className = className;
}
/**
* Convert the specified string to a String Literal and print it,
* adding the surrounding double-quotes and escaping characters
* as necessary.
*/
void printStringLiteral(String string) {
StringTools.convertToJavaStringLiteralOn(string, this);
}
void printVisibility(String visibilityModifier) {
if (visibilityModifier.length() != 0) {
this.print(visibilityModifier);
this.print(' ');
}
}
void printAnnotation(String annotationName) {
this.print('@');
this.printTypeDeclaration(annotationName);
}
void printTypeDeclaration(String typeDeclaration) {
this.print(this.buildImportedTypeDeclaration(typeDeclaration));
}
/**
* Return the specified class's "imported" name.
* The class declaration must be of the form:
* "int"
* "int[]" (not "[I")
* "java.lang.Object"
* "java.lang.Object[]" (not "[Ljava.lang.Object;")
* "java.util.Map.Entry" (not "java.util.Map$Entry")
* "java.util.Map.Entry[][]" (not "[[Ljava.util.Map$Entry;")
*/
private String buildImportedTypeDeclaration(String typeDeclaration) {
if (this.typeDeclarationIsMemberClass(typeDeclaration)) {
// no need for an import, just return the partially-qualified name
return this.buildMemberClassTypeDeclaration(typeDeclaration);
}
int last = typeDeclaration.lastIndexOf('.');
String pkg = (last == -1) ? "" : typeDeclaration.substring(0, last); //$NON-NLS-1$
String shortTypeDeclaration = typeDeclaration.substring(last + 1);
String shortElementTypeName = shortTypeDeclaration;
while (shortElementTypeName.endsWith("[]")) { //$NON-NLS-1$
shortElementTypeName = shortElementTypeName.substring(0, shortElementTypeName.length() - 2);
}
String prev = this.imports.get(shortElementTypeName);
if (prev == null) {
// this is the first class with this short element type name
this.imports.put(shortElementTypeName, pkg);
return shortTypeDeclaration;
}
if (prev.equals(pkg)) {
// this element type has already been imported
return shortTypeDeclaration;
}
// another class with the same short element type name has been
// previously imported, so this one must be used fully-qualified
return typeDeclaration;
}
/**
* e.g. "foo.bar.Employee.PK" will return true
*/
private boolean typeDeclarationIsMemberClass(String typeDeclaration) {
return (typeDeclaration.length() > this.className.length())
&& typeDeclaration.startsWith(this.className)
&& (typeDeclaration.charAt(this.className.length()) == '.');
}
/**
* e.g. "foo.bar.Employee.PK" will return "Employee.PK"
* this prevents collisions with other imported classes (e.g. "joo.jar.PK")
*/
private String buildMemberClassTypeDeclaration(String typeDeclaration) {
int index = this.packageName.length();
if (index != 0) {
index++; // bump past the '.'
}
return typeDeclaration.substring(index);
}
private Iterator<Map.Entry<String, String>> sortedImportEntries() {
TreeSet<Map.Entry<String, String>> sortedImports = new TreeSet<Map.Entry<String, String>>(this.buildImportEntriesComparator());
sortedImports.addAll(this.imports.entrySet());
return sortedImports.iterator();
}
private Comparator<Map.Entry<String, String>> buildImportEntriesComparator() {
return new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> e1, Map.Entry<String, String> e2) {
Collator collator = Collator.getInstance();
int pkg = collator.compare(e1.getValue(), e2.getValue());
return (pkg == 0) ? collator.compare(e1.getKey(), e2.getKey()) : pkg;
}
};
}
void printField(String fieldName, String typeDeclaration, String visibility) {
this.printVisibility(visibility);
this.printTypeDeclaration(typeDeclaration);
this.print(' ');
this.print(fieldName);
this.print(';');
this.println();
this.println();
}
void printParameterizedField(String fieldName, String typeDeclaration, String parameterTypeDeclaration, String visibility) {
this.printVisibility(visibility);
this.printTypeDeclaration(typeDeclaration);
this.print('<');
this.printTypeDeclaration(parameterTypeDeclaration);
this.print('>');
this.print(' ');
this.print(fieldName);
this.print(';');
this.println();
this.println();
}
void printGetterAndSetter(String propertyName, String typeDeclaration, String visibility) {
this.printGetter(propertyName, typeDeclaration, visibility);
this.println();
this.println();
this.printSetter(propertyName, typeDeclaration, visibility);
this.println();
this.println();
}
private void printGetter(String propertyName, String typeDeclaration, String visibility) {
this.printVisibility(visibility);
this.printTypeDeclaration(typeDeclaration);
this.print(' ');
this.print(typeDeclaration.equals("boolean") ? "is" : "get"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
this.print(StringTools.capitalize(propertyName));
this.print("() {"); //$NON-NLS-1$
this.println();
this.indent();
this.print("return this."); //$NON-NLS-1$
this.print(propertyName);
this.print(';');
this.println();
this.undent();
this.print('}');
}
private void printSetter(String propertyName, String typeDeclaration, String visibility) {
this.printVisibility(visibility);
this.print("void set"); //$NON-NLS-1$
this.print(StringTools.capitalize(propertyName));
this.print('(');
this.printTypeDeclaration(typeDeclaration);
this.print(' ');
this.print(propertyName);
this.print(") {"); //$NON-NLS-1$
this.println();
this.indent();
this.print("this."); //$NON-NLS-1$
this.print(propertyName);
this.print(" = "); //$NON-NLS-1$
this.print(propertyName);
this.print(';');
this.println();
this.undent();
this.print('}');
}
void printCollectionGetterAndSetter(String propertyName, String collectionTypeDeclaration, String elementTypeDeclaration, String visibility) {
this.printCollectionGetter(propertyName, collectionTypeDeclaration, elementTypeDeclaration, visibility);
this.println();
this.println();
this.printCollectionSetter(propertyName, collectionTypeDeclaration, elementTypeDeclaration, visibility);
this.println();
this.println();
}
private void printCollectionGetter(String propertyName, String collectionTypeDeclaration, String elementTypeDeclaration, String visibility) {
this.printVisibility(visibility);
this.printTypeDeclaration(collectionTypeDeclaration);
this.print('<');
this.printTypeDeclaration(elementTypeDeclaration);
this.print("> get"); //$NON-NLS-1$
this.print(StringTools.capitalize(propertyName));
this.print("() {"); //$NON-NLS-1$
this.println();
this.indent();
this.print("return this."); //$NON-NLS-1$
this.print(propertyName);
this.print(';');
this.println();
this.undent();
this.print('}');
}
private void printCollectionSetter(String propertyName, String collectionTypeDeclaration, String elementTypeDeclaration, String visibility) {
this.printVisibility(visibility);
this.print("void set"); //$NON-NLS-1$
this.print(StringTools.capitalize(propertyName));
this.print('(');
this.printTypeDeclaration(collectionTypeDeclaration);
this.print('<');
this.printTypeDeclaration(elementTypeDeclaration);
this.print('>');
this.print(' ');
this.print(propertyName);
this.print(") {"); //$NON-NLS-1$
this.println();
this.indent();
this.print("this."); //$NON-NLS-1$
this.print(propertyName);
this.print(" = "); //$NON-NLS-1$
this.print(propertyName);
this.print(';');
this.println();
this.undent();
this.print('}');
}
// ********** BodySource implementation **********
public Iterator<Map.Entry<String, String>> importEntries() {
return new FilteringIterator<Map.Entry<String, String>>(this.sortedImportEntries()) {
@Override
protected boolean accept(Map.Entry<String, String> next) {
String pkg = next.getValue();
if (pkg.equals("") //$NON-NLS-1$
|| pkg.equals("java.lang") //$NON-NLS-1$
|| pkg.equals(EntitySourceWriter.this.packageName)) {
return false;
}
return true;
}
};
}
public String getSource() {
return this.out.toString();
}
public int length() {
return ((StringWriter) this.out).getBuffer().length();
}
}
// ********** config **********
public static class Config {
private boolean convertToJavaStyleIdentifiers = true;
private boolean propertyAccessType = false; // as opposed to "field"
private String collectionTypeName = Set.class.getName();
private String collectionAttributeNameSuffix = "Collection"; // e.g. "private Set<Foo> fooCollection" //$NON-NLS-1$
private int fieldVisibility = Modifier.PRIVATE;
private int methodVisibility = Modifier.PUBLIC;
private boolean generateGettersAndSetters = true;
private boolean generateDefaultConstructor = true;
private boolean serializable = true;
private boolean generateSerialVersionUID = true;
private boolean generateEmbeddedIdForCompoundPK = true; // as opposed to IdClass
private String embeddedIdAttributeName = "pk"; //$NON-NLS-1$
private String primaryKeyMemberClassName = "PK"; //$NON-NLS-1$
/**
* key = table
* value = entity name
*/
private HashMap<Table, String> tables = new HashMap<Table, String>();
private DatabaseAnnotationNameBuilder databaseAnnotationNameBuilder = DatabaseAnnotationNameBuilder.Default.INSTANCE;
private OverwriteConfirmer overwriteConfirmer = OverwriteConfirmer.Never.INSTANCE;
public static final int PRIVATE = 0;
public static final int PACKAGE = 1;
public static final int PROTECTED = 2;
public static final int PUBLIC = 3;
public boolean convertToJavaStyleIdentifiers() {
return this.convertToJavaStyleIdentifiers;
}
public void setConvertToJavaStyleIdentifiers(boolean convertToJavaStyleIdentifiers) {
this.convertToJavaStyleIdentifiers = convertToJavaStyleIdentifiers;
}
public boolean propertyAccessType() {
return this.propertyAccessType;
}
public void setPropertyAccessType(boolean propertyAccessType) {
this.propertyAccessType = propertyAccessType;
}
public boolean fieldAccessType() {
return ! this.propertyAccessType;
}
public void setFieldAccessType(boolean fieldAccessType) {
this.propertyAccessType = ! fieldAccessType;
}
public String getCollectionTypeName() {
return this.collectionTypeName;
}
public void setCollectionTypeName(String collectionTypeName) {
this.checkRequiredString(collectionTypeName, "collection type name is required"); //$NON-NLS-1$
this.collectionTypeName = collectionTypeName;
}
public String getCollectionAttributeNameSuffix() {
return this.collectionAttributeNameSuffix;
}
public void setCollectionAttributeNameSuffix(String collectionAttributeNameSuffix) {
this.collectionAttributeNameSuffix = collectionAttributeNameSuffix;
}
public int getFieldVisibility() {
return this.fieldVisibility;
}
/** entity fields cannot be 'public' */
public void setFieldVisibility(int fieldVisibility) {
switch (fieldVisibility) {
case PRIVATE:
case PACKAGE:
case PROTECTED:
this.fieldVisibility = fieldVisibility;
break;
default:
throw new IllegalArgumentException("invalid field visibility: " + fieldVisibility); //$NON-NLS-1$
}
}
String getFieldVisibilityClause() {
switch (this.fieldVisibility) {
case PRIVATE:
return "private"; //$NON-NLS-1$
case PACKAGE:
return ""; //$NON-NLS-1$
case PROTECTED:
return "protected"; //$NON-NLS-1$
default:
throw new IllegalStateException("invalid field visibility: " + this.fieldVisibility); //$NON-NLS-1$
}
}
public int getMethodVisibility() {
return this.methodVisibility;
}
/** entity properties must be 'public' or 'protected' */
public void setMethodVisibility(int methodVisibility) {
switch (methodVisibility) {
case PROTECTED:
case PUBLIC:
this.methodVisibility = methodVisibility;
break;
default:
throw new IllegalArgumentException("invalid method visibility: " + methodVisibility); //$NON-NLS-1$
}
}
String getMethodVisibilityClause() {
switch (this.methodVisibility) {
case PROTECTED:
return "protected"; //$NON-NLS-1$
case PUBLIC:
return "public"; //$NON-NLS-1$
default:
throw new IllegalStateException("invalid method visibility: " + this.methodVisibility); //$NON-NLS-1$
}
}
public boolean generateGettersAndSetters() {
return this.generateGettersAndSetters;
}
public void setGenerateGettersAndSetters(boolean generateGettersAndSetters) {
this.generateGettersAndSetters = generateGettersAndSetters;
}
public boolean generateDefaultConstructor() {
return this.generateDefaultConstructor;
}
public void setGenerateDefaultConstructor(boolean generateDefaultConstructor) {
this.generateDefaultConstructor = generateDefaultConstructor;
}
public boolean serializable() {
return this.serializable;
}
public void setSerializable(boolean serializable) {
this.serializable = serializable;
}
public boolean generateSerialVersionUID() {
return this.generateSerialVersionUID;
}
public void setGenerateSerialVersionUID(boolean generateSerialVersionUID) {
this.generateSerialVersionUID = generateSerialVersionUID;
}
public boolean generateEmbeddedIdForCompoundPK() {
return this.generateEmbeddedIdForCompoundPK;
}
public void setGenerateEmbeddedIdForCompoundPK(boolean generateEmbeddedIdForCompoundPK) {
this.generateEmbeddedIdForCompoundPK = generateEmbeddedIdForCompoundPK;
}
public boolean generateIdClassForCompoundPK() {
return ! this.generateEmbeddedIdForCompoundPK;
}
public void setGenerateIdClassForCompoundPK(boolean generateIdClassForCompoundPK) {
this.generateEmbeddedIdForCompoundPK = ! generateIdClassForCompoundPK;
}
public String getEmbeddedIdAttributeName() {
return this.embeddedIdAttributeName;
}
public void setEmbeddedIdAttributeName(String embeddedIdAttributeName) {
this.checkRequiredString(embeddedIdAttributeName, "EmbeddedId attribute name is required"); //$NON-NLS-1$
this.embeddedIdAttributeName = embeddedIdAttributeName;
}
public String getPrimaryKeyMemberClassName() {
return this.primaryKeyMemberClassName;
}
public void setPrimaryKeyMemberClassName(String primaryKeyMemberClassName) {
this.checkRequiredString(primaryKeyMemberClassName, "primary key member class name is required"); //$NON-NLS-1$
this.primaryKeyMemberClassName = primaryKeyMemberClassName;
}
String getEntityName(Table table) {
return this.tables.get(table);
}
Iterator<Table> tables() {
return this.tables.keySet().iterator();
}
int tablesSize() {
return this.tables.size();
}
public void addTable(Table table, String entityName) {
if (table == null) {
throw new NullPointerException("table is required"); //$NON-NLS-1$
}
this.checkRequiredString(entityName, "entity name is required"); //$NON-NLS-1$
if (this.tables.containsKey(table)) {
throw new IllegalArgumentException("duplicate table: " + table.getName()); //$NON-NLS-1$
}
if (this.tables.values().contains(entityName)) {
throw new IllegalArgumentException("duplicate entity name: " + entityName); //$NON-NLS-1$
}
if ( ! NameTools.stringConsistsOfJavaIdentifierCharacters(entityName)) {
throw new IllegalArgumentException("entity name is not a valid Java identifier: " + entityName); //$NON-NLS-1$
}
if (NameTools.JAVA_RESERVED_WORDS_SET.contains(entityName)) {
throw new IllegalArgumentException("entity name is a Java reserved word: " + entityName); //$NON-NLS-1$
}
this.tables.put(table, entityName);
}
public DatabaseAnnotationNameBuilder getDatabaseAnnotationNameBuilder() {
return this.databaseAnnotationNameBuilder;
}
public void setDatabaseAnnotationNameBuilder(DatabaseAnnotationNameBuilder databaseAnnotationNameBuilder) {
if (databaseAnnotationNameBuilder == null) {
throw new NullPointerException("database annotation name builder is required"); //$NON-NLS-1$
}
this.databaseAnnotationNameBuilder = databaseAnnotationNameBuilder;
}
public OverwriteConfirmer getOverwriteConfirmer() {
return this.overwriteConfirmer;
}
public void setOverwriteConfirmer(OverwriteConfirmer overwriteConfirmer) {
if (overwriteConfirmer == null) {
throw new NullPointerException("overwrite confirmer is required"); //$NON-NLS-1$
}
this.overwriteConfirmer = overwriteConfirmer;
}
private void checkRequiredString(String string, String message) {
if ((string == null) || (string.length() == 0)) {
throw new IllegalArgumentException(message);
}
}
}
// ********** overwrite confirmer **********
public static interface OverwriteConfirmer {
/**
* Return whether the entity generator should overwrite the specified
* file.
*/
boolean overwrite(String className);
final class Always implements OverwriteConfirmer {
public static final OverwriteConfirmer INSTANCE = new Always();
public static OverwriteConfirmer instance() {
return INSTANCE;
}
// ensure single instance
private Always() {
super();
}
// everything will be overwritten
public boolean overwrite(String arg0) {
return true;
}
@Override
public String toString() {
return "OverwriteConfirmer.Always"; //$NON-NLS-1$
}
}
final class Never implements OverwriteConfirmer {
public static final OverwriteConfirmer INSTANCE = new Never();
public static OverwriteConfirmer instance() {
return INSTANCE;
}
// ensure single instance
private Never() {
super();
}
// nothing will be overwritten
public boolean overwrite(String arg0) {
return false;
}
@Override
public String toString() {
return "OverwriteConfirmer.Never"; //$NON-NLS-1$
}
}
}
// ********** annotation name builder **********
/**
* Provide a pluggable way to determine whether and how the entity generator
* prints the names of various database objects.
*/
public static interface DatabaseAnnotationNameBuilder {
/**
* Given the name of an entity and the table to which it is mapped,
* build and return a string to be used as the value for the entity's
* Table annotation's 'name' element. Return null if the entity
* maps to the table by default.
*/
String buildTableAnnotationName(String entityName, Table table);
/**
* Given the name of an attribute (field or property) and the column
* to which it is mapped,
* build and return a string to be used as the value for the attribute's
* Column annotation's 'name' element. Return null if the attribute
* maps to the column by default.
*/
String buildColumnAnnotationName(String attributeName, Column column);
/**
* Given the name of an attribute (field or property) and the
* many-to-one or many-to-many foreign key to which it is mapped,
* build and return a string to be used as the value for the attribute's
* JoinColumn annotation's 'name' element. Return null if the attribute
* maps to the join column by default.
* The specified foreign key consists of a single column pair whose
* referenced column is the single-column primary key of the foreign
* key's referenced table.
*/
String buildJoinColumnAnnotationName(String attributeName, ForeignKey foreignKey);
/**
* Build and return a string to be used as the value for a JoinColumn
* annotation's 'name' or 'referencedColumnName' element.
* This is called for many-to-one and many-to-many mappings when
* the default join column name and/or referenced column name are/is
* not applicable.
* @see buildJoinColumnAnnotationName(String, ForeignKey)
*/
String buildJoinColumnAnnotationName(Column column);
/**
* Build and return a string to be used as the value for a JoinTable
* annotation's 'name' element.
* This is called for many-to-many mappings when the default
* join table name is not applicable.
*/
String buildJoinTableAnnotationName(Table table);
/**
* The default implementation simple returns the database object's name,
* unaltered.
*/
final class Default implements DatabaseAnnotationNameBuilder {
public static final DatabaseAnnotationNameBuilder INSTANCE = new Default();
public static DatabaseAnnotationNameBuilder instance() {
return INSTANCE;
}
// ensure single instance
private Default() {
super();
}
public String buildTableAnnotationName(String entityName, Table table) {
return table.getName();
}
public String buildColumnAnnotationName(String attributeName, Column column) {
return column.getName();
}
public String buildJoinColumnAnnotationName(String attributeName, ForeignKey foreignKey) {
return foreignKey.getColumnPair().getBaseColumn().getName();
}
public String buildJoinColumnAnnotationName(Column column) {
return column.getName();
}
public String buildJoinTableAnnotationName(Table table) {
return table.getName();
}
@Override
public String toString() {
return "DatabaseAnnotationNameBuilder.Default"; //$NON-NLS-1$
}
}
}
}