/*******************************************************************************
* Copyright (c) 2010 Denis Solonenko.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Denis Solonenko - initial API and implementation
******************************************************************************/
package ru.orangesoftware.orb;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.PersistenceException;
class EntityDefinition {
static class Builder {
private final Class<?> clazz;
private Constructor<?> constructor;
private String tableName;
private FieldInfo idField;
private final List<FieldInfo> fields = new LinkedList<FieldInfo>();
Builder(Class<?> clazz) {
this.clazz = clazz;
}
public void withConstructor(Constructor<?> constructor) {
this.constructor = constructor;
}
Builder withTable(String tableName) {
this.tableName = tableName;
return this;
}
Builder withIdField(FieldInfo fi) {
idField = fi;
fields.add(fi);
return this;
}
Builder withField(FieldInfo fi) {
fields.add(fi);
return this;
}
EntityDefinition create() {
if (tableName == null) {
tableName = clazz.getSimpleName().toUpperCase();
}
return new EntityDefinition(constructor, tableName, idField, fields.toArray(new FieldInfo[fields.size()]));
}
}
static final String DEFAULT_ID_COLUMN_NAME = "_id";
final Constructor<?> constructor;
final String tableName;
final FieldInfo idField;
final FieldInfo[] fields;
// final String[] primitiveColumns;
// final JoinEntity[] joinEntities;
final String sqlQuery;
final HashMap<String, FieldInfo> fieldToInfoMap = new HashMap<String, FieldInfo>();
private EntityDefinition(Constructor<?> constructor, String tableName, FieldInfo idField, FieldInfo[] fields) {
this.constructor = constructor;
this.tableName = tableName;
this.idField = idField;
this.fields = fields;
this.sqlQuery = prepareSqlQuery();
prepareColumns();
// this.primitiveColumns = prepareColumns();
// this.joinEntities = prepareJoinEntities();
}
private void prepareColumns() {
FieldInfo[] fields = this.fields;
for (FieldInfo f : fields) {
fieldToInfoMap.put(f.field.getName(), f);
}
}
// private JoinEntity[] prepareJoinEntities() {
// LinkedList<JoinEntity> entities = new LinkedList<JoinEntity>();
// prepareJoinEntities(entities, this, 0, true);
// return entities.toArray(new JoinEntity[entities.size()]);
// }
//
// private int prepareJoinEntities(LinkedList<JoinEntity> entities, EntityDefinition ed, int parentIndex, boolean required) {
// int index = parentIndex;
// FieldInfo[] fields = ed.fields;
// int count = fields.length;
// for (int i=0; i<count; i++) {
// FieldInfo f = fields[i];
// if (!f.type.isPrimitive()) {
// boolean isRequired = required && f.required;
// EntityDefinition eed = EntityManager.getEntityDefinitionOrThrow(f.field.getType());
// JoinEntity e = new JoinEntity(f, eed, ++index, parentIndex, isRequired);
// entities.add(e);
// index = prepareJoinEntities(entities, eed, index, isRequired);
// }
// }
// return index;
// }
public long getId(Object entity) {
try {
return idField.field.getLong(entity);
} catch (Exception e) {
throw new PersistenceException("Unable to get id from "+entity, e);
}
}
public void setId(Object entity, long id) {
try {
idField.field.setLong(entity, id);
} catch (Exception e) {
throw new PersistenceException("Unable to set id for "+entity, e);
}
}
public String getColumnForField(String field) {
if (field.indexOf('.') > 0) {
String[] path = field.split("\\.");
StringBuilder e = new StringBuilder("e");
int count = path.length;
EntityDefinition ed = this;
for (int i=0; i<count; i++) {
String f = path[i];
FieldInfo fi = ed.getFieldInfo(f);
if (fi.type.isPrimitive()) {
e.append("_").append(fi.columnName);
break;
} else {
e.append(fi.index);
ed = EntityManager.getEntityDefinitionOrThrow(fi.field.getType());
}
}
return e.toString();
} else {
FieldInfo fi = getFieldInfo(field);
return "e_"+fi.columnName;
}
}
private FieldInfo getFieldInfo(String field) {
FieldInfo f = fieldToInfoMap.get(field);
if (f == null) {
throw new IllegalArgumentException("Unknown field ["+field+"] for "+constructor.getDeclaringClass());
}
return f;
}
protected String prepareSqlQuery() {
StringBuilder sb1 = new StringBuilder("select ");
sb1.append("e").append(".").append(idField.columnName).append(" as ").append(DEFAULT_ID_COLUMN_NAME);
StringBuilder sb2 = new StringBuilder();
sb2.append(" from ").append(tableName).append(" as e");
prepareSqlQuery(this, sb1, sb2, "e", true);
return sb1.append(sb2).toString();
}
protected void prepareSqlQuery(EntityDefinition ed, StringBuilder sbColumns, StringBuilder sbJoins, String pe, boolean required) {
FieldInfo[] fields = ed.fields;
for (FieldInfo f : fields) {
if (f.type.isPrimitive()) {
appendColumn(sbColumns, pe, f.columnName);
} else {
String e = pe+f.index;
boolean isRequired = required & f.required;
EntityDefinition edJoin = EntityManager.getEntityDefinitionOrThrow(f.field.getType());
sbJoins.append(isRequired ? " inner join " : " left outer join ").append(edJoin.tableName).append(" as ").append(e);
sbJoins.append(" on ").append(e).append(".").append(ed.idField.columnName).append("=").append(pe).append(".").append(f.columnName);
prepareSqlQuery(edJoin, sbColumns, sbJoins, e, isRequired);
}
}
}
// protected String prepareSqlQuery() {
// String e0 = "e0";
// StringBuilder sb1 = new StringBuilder("select ");
// sb1.append(e0).append(".").append(idField.columnName).append(" as _id");
// StringBuilder sb2 = new StringBuilder();
// appendColumns(sb1, e0, primitiveColumns);
// JoinEntity[] entities = this.joinEntities;
// for (JoinEntity entity : entities) {
// String e = "e"+entity.index;
// String pe = "e"+entity.parentIndex;
// FieldInfo f = entity.field;
// EntityDefinition ed = entity.entity;
// sb2.append(entity.required ? " inner join " : " outer join ").append(ed.tableName).append(" as ").append(e);
// sb2.append(" on ").append(e).append(".").append(ed.idField.columnName).append("=").append(pe).append(".").append(f.columnName);
// appendColumns(sb1, e, ed.primitiveColumns);
// }
// sb1.append(" from ").append(tableName).append(" as ").append(e0).append(" ").append(sb2.toString());
// return sb1.toString();
// }
//
// private void appendColumns(StringBuilder sb, String e, String[] columns) {
// for (String c : columns) {
// appendColumn(sb, e, c);
// }
// }
private void appendColumn(StringBuilder sb, String e, String c) {
sb.append(", ").append(e).append(".").append(c).append(" as ").append(e).append("_").append(c);
}
}