org.greenrobot.greendao.generator.Entity Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of greendao-generator Show documentation
Show all versions of greendao-generator Show documentation
Code generator for greenDAO, the light and fast ORM for Android
/*
* Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org)
*
* This file is part of greenDAO Generator.
*
* greenDAO Generator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* greenDAO Generator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with greenDAO Generator. If not, see .
*/
package org.greenrobot.greendao.generator;
import org.greenrobot.greendao.generator.Property.PropertyBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**
* Model class for an entity: a Java data object mapped to a data base table. A new entity is added to a {@link Schema}
* by the method {@link Schema#addEntity(String)} (there is no public constructor for {@link Entity} itself).
*
Use the various addXXX methods to add entity properties, indexes, and relations to other entities (addToOne,
* addToMany).
There are further configuration possibilities: - {@link
* Entity#implementsInterface(String...)} and {@link #implementsSerializable()} to specify interfaces the entity will
* implement
- {@link #setSuperclass(String)} to specify a class of which the entity will extend from
* - Various setXXX methods
*
* @see Modelling Entities (Documentation page)
* @see Relations (Documentation page)
*/
@SuppressWarnings("unused")
public class Entity {
private final Schema schema;
private final String className;
private final List properties;
private List propertiesColumns;
private final List propertiesPk;
private final List propertiesNonPk;
private final Set propertyNames;
private final List indexes;
private final List multiIndexes;
private final List toOneRelations;
private final List toManyRelations;
private final List incomingToManyRelations;
private final Collection additionalImportsEntity;
private final Collection additionalImportsDao;
private final List interfacesToImplement;
private final List contentProviders;
private String dbName;
private boolean nonDefaultDbName;
private String classNameDao;
private String classNameTest;
private String javaPackage;
private String javaPackageDao;
private String javaPackageTest;
private Property pkProperty;
private String pkType;
private String superclass;
private String javaDoc;
private String codeBeforeClass;
private boolean protobuf;
private boolean constructors;
private boolean skipGeneration;
private boolean skipGenerationTest;
private boolean skipCreationInDb;
private Boolean active;
private Boolean hasKeepSections;
Entity(Schema schema, String className) {
this.schema = schema;
this.className = className;
properties = new ArrayList<>();
propertiesPk = new ArrayList<>();
propertiesNonPk = new ArrayList<>();
propertyNames = new HashSet<>();
indexes = new ArrayList<>();
multiIndexes = new ArrayList<>();
toOneRelations = new ArrayList<>();
toManyRelations = new ArrayList<>();
incomingToManyRelations = new ArrayList<>();
additionalImportsEntity = new TreeSet<>();
additionalImportsDao = new TreeSet<>();
interfacesToImplement = new ArrayList<>();
contentProviders = new ArrayList<>();
constructors = true;
}
public PropertyBuilder addBooleanProperty(String propertyName) {
return addProperty(PropertyType.Boolean, propertyName);
}
public PropertyBuilder addByteProperty(String propertyName) {
return addProperty(PropertyType.Byte, propertyName);
}
public PropertyBuilder addShortProperty(String propertyName) {
return addProperty(PropertyType.Short, propertyName);
}
public PropertyBuilder addIntProperty(String propertyName) {
return addProperty(PropertyType.Int, propertyName);
}
public PropertyBuilder addLongProperty(String propertyName) {
return addProperty(PropertyType.Long, propertyName);
}
public PropertyBuilder addFloatProperty(String propertyName) {
return addProperty(PropertyType.Float, propertyName);
}
public PropertyBuilder addDoubleProperty(String propertyName) {
return addProperty(PropertyType.Double, propertyName);
}
public PropertyBuilder addByteArrayProperty(String propertyName) {
return addProperty(PropertyType.ByteArray, propertyName);
}
public PropertyBuilder addStringProperty(String propertyName) {
return addProperty(PropertyType.String, propertyName);
}
public PropertyBuilder addDateProperty(String propertyName) {
return addProperty(PropertyType.Date, propertyName);
}
public PropertyBuilder addProperty(PropertyType propertyType, String propertyName) {
if (!propertyNames.add(propertyName)) {
throw new RuntimeException("Property already defined: " + propertyName);
}
PropertyBuilder builder = new PropertyBuilder(schema, this, propertyType, propertyName);
properties.add(builder.getProperty());
return builder;
}
/** Adds a standard _id column required by standard Android classes, e.g. list adapters. */
public PropertyBuilder addIdProperty() {
PropertyBuilder builder = addLongProperty("id");
builder.dbName("_id").primaryKey();
return builder;
}
/** Adds a to-many relationship; the target entity is joined to the PK property of this entity (typically the ID). */
public ToMany addToMany(Entity target, Property targetProperty) {
Property[] targetProperties = {targetProperty};
return addToMany(null, target, targetProperties);
}
/**
* Convenience method for {@link Entity#addToMany(Entity, Property)} with a subsequent call to {@link
* ToMany#setName(String)}.
*/
public ToMany addToMany(Entity target, Property targetProperty, String name) {
ToMany toMany = addToMany(target, targetProperty);
toMany.setName(name);
return toMany;
}
/**
* Add a to-many relationship; the target entity is joined using the given target property (of the target entity)
* and given source property (of this entity).
*/
public ToMany addToMany(Property sourceProperty, Entity target, Property targetProperty) {
Property[] sourceProperties = {sourceProperty};
Property[] targetProperties = {targetProperty};
return addToMany(sourceProperties, target, targetProperties);
}
public ToMany addToMany(Property[] sourceProperties, Entity target, Property[] targetProperties) {
if (protobuf) {
throw new IllegalStateException("Protobuf entities do not support relations, currently");
}
ToMany toMany = new ToMany(schema, this, sourceProperties, target, targetProperties);
toManyRelations.add(toMany);
target.incomingToManyRelations.add(toMany);
return toMany;
}
public ToManyWithJoinEntity addToMany(Entity target, Entity joinEntity, Property id1, Property id2) {
ToManyWithJoinEntity toMany = new ToManyWithJoinEntity(schema, this, target, joinEntity, id1, id2);
toManyRelations.add(toMany);
target.incomingToManyRelations.add(toMany);
return toMany;
}
/**
* Adds a to-one relationship to the given target entity using the given given foreign key property (which belongs
* to this entity).
*/
public ToOne addToOne(Entity target, Property fkProperty) {
if (protobuf) {
throw new IllegalStateException("Protobuf entities do not support realtions, currently");
}
Property[] fkProperties = {fkProperty};
ToOne toOne = new ToOne(schema, this, target, fkProperties, true);
toOneRelations.add(toOne);
return toOne;
}
/** Convenience for {@link #addToOne(Entity, Property)} with a subsequent call to {@link ToOne#setName(String)}. */
public ToOne addToOne(Entity target, Property fkProperty, String name) {
ToOne toOne = addToOne(target, fkProperty);
toOne.setName(name);
return toOne;
}
public ToOne addToOneWithoutProperty(String name, Entity target, String fkColumnName) {
return addToOneWithoutProperty(name, target, fkColumnName, false, false);
}
public ToOne addToOneWithoutProperty(String name, Entity target, String fkColumnName, boolean notNull,
boolean unique) {
PropertyBuilder propertyBuilder = new PropertyBuilder(schema, this, null, name);
if (notNull) {
propertyBuilder.notNull();
}
if (unique) {
propertyBuilder.unique();
}
propertyBuilder.dbName(fkColumnName);
Property column = propertyBuilder.getProperty();
Property[] fkColumns = {column};
ToOne toOne = new ToOne(schema, this, target, fkColumns, false);
toOne.setName(name);
toOneRelations.add(toOne);
return toOne;
}
protected void addIncomingToMany(ToMany toMany) {
incomingToManyRelations.add(toMany);
}
public ContentProvider addContentProvider() {
List entities = new ArrayList<>();
entities.add(this);
ContentProvider contentProvider = new ContentProvider(schema, entities);
contentProviders.add(contentProvider);
return contentProvider;
}
/** Adds a new index to the entity. */
public Entity addIndex(Index index) {
indexes.add(index);
return this;
}
public Entity addImport(String additionalImport) {
additionalImportsEntity.add(additionalImport);
return this;
}
/** The entity is represented by a protocol buffers object. Requires some special actions like using builders. */
Entity useProtobuf() {
protobuf = true;
return this;
}
public boolean isProtobuf() {
return protobuf;
}
public Schema getSchema() {
return schema;
}
public String getDbName() {
return dbName;
}
@Deprecated
/**
* @deprecated Use setDbName
*/
public void setTableName(String tableName) {
setDbName(tableName);
}
public void setDbName(String dbName) {
this.dbName = dbName;
this.nonDefaultDbName = dbName != null;
}
public String getClassName() {
return className;
}
public List getProperties() {
return properties;
}
public List getPropertiesColumns() {
return propertiesColumns;
}
public String getJavaPackage() {
return javaPackage;
}
public void setJavaPackage(String javaPackage) {
this.javaPackage = javaPackage;
}
public String getJavaPackageDao() {
return javaPackageDao;
}
public void setJavaPackageDao(String javaPackageDao) {
this.javaPackageDao = javaPackageDao;
}
public String getClassNameDao() {
return classNameDao;
}
public void setClassNameDao(String classNameDao) {
this.classNameDao = classNameDao;
}
public String getClassNameTest() {
return classNameTest;
}
public void setClassNameTest(String classNameTest) {
this.classNameTest = classNameTest;
}
public String getJavaPackageTest() {
return javaPackageTest;
}
public void setJavaPackageTest(String javaPackageTest) {
this.javaPackageTest = javaPackageTest;
}
/** Internal property used by templates, don't use during entity definition. */
public List getPropertiesPk() {
return propertiesPk;
}
/** Internal property used by templates, don't use during entity definition. */
public List getPropertiesNonPk() {
return propertiesNonPk;
}
/** Internal property used by templates, don't use during entity definition. */
public Property getPkProperty() {
return pkProperty;
}
public List getIndexes() {
return indexes;
}
/** Internal property used by templates, don't use during entity definition. */
public String getPkType() {
return pkType;
}
public boolean isConstructors() {
return constructors;
}
/** Flag to define if constructors should be generated. */
public void setConstructors(boolean constructors) {
this.constructors = constructors;
}
public boolean isSkipGeneration() {
return skipGeneration;
}
/**
* Flag if the entity's code generation should be skipped. E.g. if you need to change the class after initial
* generation.
*/
public void setSkipGeneration(boolean skipGeneration) {
this.skipGeneration = skipGeneration;
}
@Deprecated
/**
* @deprecated Use setSkipCreationInDb
*/
public void setSkipTableCreation(boolean skipTableCreation) {
setSkipCreationInDb(skipTableCreation);
}
/** Flag if CREATE & DROP TABLE scripts should be skipped in Dao. */
public void setSkipCreationInDb(boolean skipCreationInDb) {
this.skipCreationInDb = skipCreationInDb;
}
public boolean isSkipCreationInDb() {
return skipCreationInDb;
}
public boolean isSkipGenerationTest() {
return skipGenerationTest;
}
public void setSkipGenerationTest(boolean skipGenerationTest) {
this.skipGenerationTest = skipGenerationTest;
}
public List getToOneRelations() {
return toOneRelations;
}
public List getToManyRelations() {
return toManyRelations;
}
public List getIncomingToManyRelations() {
return incomingToManyRelations;
}
/**
* Entities with relations are active, but this method allows to make the entities active even if it does not have
* relations.
*/
public void setActive(Boolean active) {
this.active = active;
}
public Boolean getActive() {
return active;
}
public Boolean getHasKeepSections() {
return hasKeepSections;
}
public Collection getAdditionalImportsEntity() {
return additionalImportsEntity;
}
public Collection getAdditionalImportsDao() {
return additionalImportsDao;
}
public void setHasKeepSections(Boolean hasKeepSections) {
this.hasKeepSections = hasKeepSections;
}
public List getInterfacesToImplement() {
return interfacesToImplement;
}
public List getContentProviders() {
return contentProviders;
}
public void implementsInterface(String... interfaces) {
for (String interfaceToImplement : interfaces) {
if (interfacesToImplement.contains(interfaceToImplement)) {
throw new RuntimeException("Interface defined more than once: " + interfaceToImplement);
}
interfacesToImplement.add(interfaceToImplement);
}
}
public void implementsSerializable() {
interfacesToImplement.add("java.io.Serializable");
}
public String getSuperclass() {
return superclass;
}
public void setSuperclass(String classToExtend) {
this.superclass = classToExtend;
}
public String getJavaDoc() {
return javaDoc;
}
public void setJavaDoc(String javaDoc) {
this.javaDoc = DaoUtil.checkConvertToJavaDoc(javaDoc, "");
}
public String getCodeBeforeClass() {
return codeBeforeClass;
}
public void setCodeBeforeClass(String codeBeforeClass) {
this.codeBeforeClass = codeBeforeClass;
}
void init2ndPass() {
init2ndPassNamesWithDefaults();
for (int i = 0; i < properties.size(); i++) {
Property property = properties.get(i);
property.setOrdinal(i);
property.init2ndPass();
if (property.isPrimaryKey()) {
propertiesPk.add(property);
} else {
propertiesNonPk.add(property);
}
}
for (int i = 0; i < indexes.size(); i++) {
final Index index = indexes.get(i);
final int propertiesSize = index.getProperties().size();
if (propertiesSize == 1) {
final Property property = index.getProperties().get(0);
property.setIndex(index);
} else if (propertiesSize > 1) {
multiIndexes.add(index);
}
}
if (propertiesPk.size() == 1) {
pkProperty = propertiesPk.get(0);
pkType = schema.mapToJavaTypeNullable(pkProperty.getPropertyType());
} else {
pkType = "Void";
}
propertiesColumns = new ArrayList<>(properties);
for (ToOne toOne : toOneRelations) {
toOne.init2ndPass();
Property[] fkProperties = toOne.getFkProperties();
for (Property fkProperty : fkProperties) {
if (!propertiesColumns.contains(fkProperty)) {
propertiesColumns.add(fkProperty);
}
}
}
for (ToManyBase toMany : toManyRelations) {
toMany.init2ndPass();
// Source Properties may not be virtual, so we do not need the following code:
// for (Property sourceProperty : toMany.getSourceProperties()) {
// if (!propertiesColumns.contains(sourceProperty)) {
// propertiesColumns.add(sourceProperty);
// }
// }
}
if (active == null) {
active = schema.isUseActiveEntitiesByDefault();
}
active |= !toOneRelations.isEmpty() || !toManyRelations.isEmpty();
if (hasKeepSections == null) {
hasKeepSections = schema.isHasKeepSectionsByDefault();
}
init2ndPassIndexNamesWithDefaults();
for (ContentProvider contentProvider : contentProviders) {
contentProvider.init2ndPass();
}
}
protected void init2ndPassNamesWithDefaults() {
if (dbName == null) {
dbName = DaoUtil.dbName(className);
nonDefaultDbName = false;
}
if (classNameDao == null) {
classNameDao = className + "Dao";
}
if (classNameTest == null) {
classNameTest = className + "Test";
}
if (javaPackage == null) {
javaPackage = schema.getDefaultJavaPackage();
}
if (javaPackageDao == null) {
javaPackageDao = schema.getDefaultJavaPackageDao();
if (javaPackageDao == null) {
javaPackageDao = javaPackage;
}
}
if (javaPackageTest == null) {
javaPackageTest = schema.getDefaultJavaPackageTest();
if (javaPackageTest == null) {
javaPackageTest = javaPackage;
}
}
}
protected void init2ndPassIndexNamesWithDefaults() {
for (int i = 0; i < indexes.size(); i++) {
Index index = indexes.get(i);
if (index.getName() == null) {
String indexName = "IDX_" + getDbName();
List properties = index.getProperties();
for (int j = 0; j < properties.size(); j++) {
Property property = properties.get(j);
indexName += "_" + property.getDbName();
if ("DESC".equalsIgnoreCase(index.getPropertiesOrder().get(j))) {
indexName += "_DESC";
}
}
// TODO can this get too long? how to shorten reliably without depending on the order (i)
index.setDefaultName(indexName);
}
}
}
void init3rdPass() {
for (Property property : properties) {
property.init3ndPass();
}
init3rdPassRelations();
init3rdPassAdditionalImports();
}
private void init3rdPassRelations() {
Set toOneNames = new HashSet<>();
for (ToOne toOne : toOneRelations) {
toOne.init3ndPass();
if (!toOneNames.add(toOne.getName().toLowerCase())) {
throw new RuntimeException("Duplicate name for " + toOne);
}
}
Set toManyNames = new HashSet<>();
for (ToManyBase toMany : toManyRelations) {
toMany.init3rdPass();
if (toMany instanceof ToMany) {
Entity targetEntity = toMany.getTargetEntity();
for (Property targetProperty : ((ToMany) toMany).getTargetProperties()) {
if (!targetEntity.propertiesColumns.contains(targetProperty)) {
targetEntity.propertiesColumns.add(targetProperty);
}
}
}
if (!toManyNames.add(toMany.getName().toLowerCase())) {
throw new RuntimeException("Duplicate name for " + toMany);
}
}
}
private void init3rdPassAdditionalImports() {
if (active && !javaPackage.equals(javaPackageDao)) {
additionalImportsEntity.add(javaPackageDao + "." + classNameDao);
}
for (ToOne toOne : toOneRelations) {
Entity targetEntity = toOne.getTargetEntity();
checkAdditionalImportsEntityTargetEntity(targetEntity);
// For deep loading
checkAdditionalImportsDaoTargetEntity(targetEntity);
}
for (ToManyBase toMany : toManyRelations) {
Entity targetEntity = toMany.getTargetEntity();
checkAdditionalImportsEntityTargetEntity(targetEntity);
}
for (ToManyBase incomingToMany : incomingToManyRelations) {
if (incomingToMany instanceof ToManyWithJoinEntity) {
final ToManyWithJoinEntity toManyWithJoinEntity = (ToManyWithJoinEntity) incomingToMany;
final Entity joinEntity = toManyWithJoinEntity.getJoinEntity();
checkAdditionalImportsDaoTargetEntity(joinEntity);
}
}
for (Property property : properties) {
String customType = property.getCustomType();
if (customType != null) {
String pack = DaoUtil.getPackageFromFullyQualified(customType);
if (pack != null && !pack.equals(javaPackage)) {
additionalImportsEntity.add(customType);
}
if (pack != null && !pack.equals(javaPackageDao)) {
additionalImportsDao.add(customType);
}
}
String converter = property.getConverter();
if (converter != null) {
String pack = DaoUtil.getPackageFromFullyQualified(converter);
if (pack != null && !pack.equals(javaPackageDao)) {
additionalImportsDao.add(converter);
}
}
}
}
private void checkAdditionalImportsEntityTargetEntity(Entity targetEntity) {
if (!targetEntity.getJavaPackage().equals(javaPackage)) {
additionalImportsEntity.add(targetEntity.getJavaPackage() + "." + targetEntity.getClassName());
}
if (!targetEntity.getJavaPackageDao().equals(javaPackage)) {
additionalImportsEntity.add(targetEntity.getJavaPackageDao() + "." + targetEntity.getClassNameDao());
}
}
private void checkAdditionalImportsDaoTargetEntity(Entity targetEntity) {
if (!targetEntity.getJavaPackage().equals(javaPackageDao)) {
additionalImportsDao.add(targetEntity.getJavaPackage() + "." + targetEntity.getClassName());
}
}
public void validatePropertyExists(Property property) {
if (!properties.contains(property)) {
throw new RuntimeException("Property " + property + " does not exist in " + this);
}
}
public List getMultiIndexes() {
return multiIndexes;
}
public boolean isNonDefaultDbName() {
return nonDefaultDbName;
}
@Override
public String toString() {
return "Entity " + className + " (package: " + javaPackage + ")";
}
}