
com.caucho.v5.ramp.vault.EntityInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of baratine Show documentation
Show all versions of baratine Show documentation
A reactive Java web server.
/*
* Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
*
* This file is part of Baratine(TM)
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Baratine 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 2 of the License, or
* (at your option) any later version.
*
* Baratine 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Baratine; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Alex Rojkov
*/
package com.caucho.v5.ramp.vault;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.logging.Logger;
import com.caucho.v5.amp.vault.IdAssetGenerator;
import com.caucho.v5.inject.AnnotationLiteral;
import com.caucho.v5.inject.type.TypeRef;
import com.caucho.v5.kraken.info.TableInfo;
import com.caucho.v5.util.L10N;
import com.caucho.v5.util.RandomUtil;
import io.baratine.db.Cursor;
import io.baratine.vault.Asset;
import io.baratine.vault.IdAsset;
class EntityInfo
{
private static final Logger log
= Logger.getLogger(EntityInfo.class.getName());
private static final L10N L = new L10N(EntityInfo.class);
private static final
HashMap,Function,IdGenerator>>> _idGenMap;
private static final Map _primitiveWrappers = new HashMap<>();
private final String _tableName;
private final FieldInfo[] _fields;
private Class _type;
private boolean _isDocument;
private Class _idType;
private IdGenerator _idGen;
private AtomicLong _idSequence = new AtomicLong();
private String _addressPrefix;
private int _saveColumns;
private boolean _isSolo;
public EntityInfo(Class type,
Class idType,
Asset table)
{
Objects.requireNonNull(type);
Objects.requireNonNull(idType);
_type = type;
_idType = idType;
_addressPrefix = "/" + type.getSimpleName();
// _addressPrefix = "/QstoreIdLongJava"; // XXX:
if (table != null && ! table.value().isEmpty()) {
_tableName = table.value();
}
else {
_tableName = type.getSimpleName();
}
_fields = introspect();
}
private FieldInfo []introspect()
{
List> fields = new ArrayList<>();
if (Void.class.equals(_idType)) {
FieldIdSolo fieldId = new FieldIdSolo<>();
_isSolo = true;
fields.add(fieldId);
}
for (Field field : _type.getDeclaredFields()) {
if (! isPersistent(field)) {
continue;
}
ColumnVault column = field.getAnnotation(ColumnVault.class);
if (column == null) {
column = makeDefaultColumn(field);
}
FieldInfo fieldInfo;
if (IdAsset.class.equals(field.getType())) {
fieldInfo = new FieldInfoIdAsset<>(field, column);
}
else {
fieldInfo = new FieldReflected<>(field, column);
}
if (! fieldInfo.isColumn()) {
_isDocument = true;
}
fields.add(fieldInfo);
}
List> ids = new ArrayList<>();
for (FieldInfo field : fields) {
if (field.isId()) {
ids.add(field);
}
}
if (ids.size() == 0) {
throw new IllegalStateException(L.l("{0} must define a primary key",
_type));
}
else if (ids.size() > 1) {
throw new IllegalStateException(L.l("too many fields declare primary key {0}",
ids.toString()));
}
if (! isIdTypeMatch(_idType, ids.get(0).getJavaType())) {
throw new IllegalStateException(L.l(
"repository defined '{0}' does not match actual @Id `{1}` on entity '{2}'",
_idType.getName(),
ids.get(0).getJavaType(),
_type.getName()));
}
ColumnVault objColumn = _type.getAnnotation(ColumnVault.class);
if (objColumn != null) {
fields.add(new FieldInfoObject(_type, objColumn));
}
FieldInfo[] fieldsArray = fields.toArray(new FieldInfo[fields.size()]);
Function,IdGenerator> idGenFun
= (Function) _idGenMap.get(_idType);
_idSequence.set(RandomUtil.getRandomLong());
IdGenerator idGen = null;
if (idGenFun != null) {
idGen = idGenFun.apply(this);
}
else {
idGen = new IdGenerator<>(this);
}
Objects.requireNonNull(idGen);
_idGen = idGen;
return fieldsArray;
}
private boolean isIdTypeMatch(Class> repIdType, Class> idType)
{
if (repIdType.equals(idType)) {
return true;
}
if (idType.isPrimitive()
&& _primitiveWrappers.get(idType).equals(repIdType)) {
return true;
}
return false;
}
public Class type()
{
return _type;
}
public Class idType()
{
return _idType;
}
public boolean isSolo()
{
return _isSolo;
}
public boolean isDocument()
{
return _isDocument;
}
private boolean isPersistent(Field field)
{
int mod = field.getModifiers();
if (Modifier.isStatic(mod))
return false;
else if (Modifier.isTransient(mod))
return false;
return true;
}
public FieldInfo id()
{
for (FieldInfo field : _fields) {
if (field.isId()) {
return (FieldInfo) field;
}
}
return null;
}
public ID id(T bean)
{
return (ID) id().getValue(bean);
}
private String prefix()
{
return _addressPrefix;
}
private int node()
{
return 0;
}
private int nodeCount()
{
return 1;
}
private long nextSequence()
{
return _idSequence.incrementAndGet();
}
public void nextId(T bean)
{
ID id = id(bean);
ID idNew = _idGen.generate(id);
if (id != idNew) {
id().setValue(bean, idNew);
}
}
public ID nextId()
{
return _idGen.generate(null);
}
public String generateAddress(ID id)
{
return _idGen.generateAddress(id);
}
public FieldInfo[] getFields()
{
return _fields;
}
public FieldInfo,?> field(String fieldName)
{
for (FieldInfo,?> field : _fields) {
if (field.columnName().equalsIgnoreCase(fieldName)) {
return field;
}
}
return null;
}
public String tableName()
{
return _tableName;
}
public int getSize()
{
return _fields.length;
}
String saveSql()
{
StringBuilder head = new StringBuilder("insert into ")
.append(tableName())
.append(" (");
StringBuilder tail = new StringBuilder(") values (");
FieldInfo[] fields = getFields();
boolean isDocument = false;
int saveColumns = 0;
for (int i = 0; i < fields.length; i++) {
FieldInfo field = getFields()[i];
if (! field.isColumn()) {
isDocument = true;
continue;
}
saveColumns++;
head.append(field.columnName());
tail.append('?');
if ((i + 1) < fields.length) {
head.append(", ");
tail.append(", ");
}
}
if (isDocument) {
saveColumns++;
head.append("__doc");
tail.append("?");
}
tail.append(')');
head.append(tail);
_saveColumns = saveColumns;
return head.toString();
}
public String loadSql()
{
StringBuilder head = new StringBuilder("select ");
StringBuilder where = new StringBuilder(" where ");
FieldInfo[] fields = getFields();
boolean isFirst = true;
for (int i = 0; i < fields.length; i++) {
FieldInfo field = fields[i];
if (field.isId()) {
where.append(field.columnName()).append(" = ?");
continue;
}
if (! field.isColumn()) {
continue;
}
if (isFirst) {
isFirst = false;
}
else {
head.append(", ");
}
head.append(field.columnName());
}
if (isDocument()) {
if (! isFirst) {
head.append(", ");
}
head.append("__doc");
}
head.append(" from ").append(tableName());
head.append(where);
return head.toString();
}
public Object[] saveValues(T entity)
{
Object []values = new Object[_saveColumns];
Map doc = null;
if (_isDocument) {
doc = new HashMap<>();
values[values.length - 1] = doc;
}
int i = 0;
for (FieldInfo field : _fields) {
if (field.isColumn()) {
values[i++] = field.toParam(field.getValue(entity));
}
else {
doc.put(field.columnName(), field.getValue(entity));
}
}
return values;
}
public Object getValue(int index, T bean)
{
Object value = _fields[index].getValue(bean);
if (value == null && _fields[index].isId()) {
value = new Long(0);
}
return value;
}
public T readObject(Cursor cursor, boolean isInitPk)
throws ReflectiveOperationException
{
Objects.requireNonNull(cursor);
FieldInfoObject fieldObject = null;
int index = 0;
for (int i = 0; i < _fields.length; i++) {
FieldInfo field = _fields[i];
if (! field.isId() || isInitPk)
index++;
if (field instanceof FieldInfoObject) {
fieldObject = (FieldInfoObject) field;
break;
}
}
T t;
if (fieldObject == null) {
t = createFromClass();
load(cursor, t, isInitPk);
}
else {
t = createFromCursor(cursor, index);
}
return t;
}
public void load(Cursor cursor, T entity)
throws IllegalAccessException
{
Objects.requireNonNull(cursor);
Objects.requireNonNull(entity);
load(cursor, entity, false);
}
private void load(Cursor cursor, T bean, boolean isInitId)
throws IllegalAccessException
{
int index = 0;
for (int i = 0; i < _fields.length; i++) {
FieldInfo field = _fields[i];
if (! isInitId && field.isId()) {
continue;
}
else if (field.isColumn()) {
index++;
field.setValue(bean, cursor, index);
}
}
if (isDocument()) {
Object doc = cursor.getObject(index + 1);
if (doc instanceof Map) {
Map docMap = (Map) doc;
for (FieldInfo field : _fields) {
field.setValueFromDocument(bean, docMap);
}
}
}
}
private T createFromClass()
throws ReflectiveOperationException
{
Constructor ctor = _type.getConstructor();
if (ctor == null)
throw new IllegalStateException(
L.l("{0} must provide default constructor", _type));
ctor.setAccessible(true);
return (T) ctor.newInstance();
}
private T createFromCursor(Cursor cursor, int index)
{
T t = null;
if (cursor != null)
t = (T) cursor.getObject(index);
return t;
}
private Asset makeDefaultTable(Class type)
{
String name = type.getSimpleName();
if (name.endsWith("Impl"))
name = name.substring(0, name.lastIndexOf("Impl"));
return new TableLiteral(name);
}
private ColumnVault makeDefaultColumn(Field field)
{
String name = field.getName();
if (name.startsWith("_"))
name = name.substring(1);
return new ColumnLiteral(name);
}
public void fillColumns(TableInfo tableInfo)
{
for (FieldInfo field : _fields) {
field.fillColumn(tableInfo);
}
_isDocument = tableInfo.column("__doc") != null;
}
public void setPk(T t, ID id)
throws IllegalAccessException
{
id().setValue(t, id);
}
public FieldInfo findFieldByType(TypeRef resultType)
{
Class> rawResult = resultType.rawClass();
for (FieldInfo field : _fields) {
if (field.isId())
continue;
Class> fieldType = field.getJavaType();
if (rawResult.equals(fieldType))
return field;
if (fieldType.isPrimitive()
&& rawResult.equals(_primitiveWrappers.get(fieldType))) {
return field;
}
}
return null;
}
@Override
public String toString()
{
return EntityInfo.class.getSimpleName()
+ "["
+ _type
+ ':'
+ tableName()
+ "]";
}
public StringBuilder selectField(FieldInfo field)
{
StringBuilder sql = new StringBuilder("select ")
.append(field.columnName())
.append(" from ").append(_tableName);
return sql;
}
private static class IdGenerator
{
private EntityInfo,?> _entity;
IdGenerator(EntityInfo,?> entity)
{
_entity = entity;
}
protected EntityInfo,?> entity()
{
return _entity;
}
public ID generate(ID id)
{
return id;
}
public String generateAddress(ID id)
{
id = generate(id);
return _entity.prefix() + "/" + id;
}
}
private static class IdGeneratorLong extends IdGenerator
{
private IdAssetGenerator _idGen;
private IdGeneratorLong(EntityInfo,?> entity)
{
super(entity);
_idGen = new IdAssetGenerator(entity.node());
}
@Override
public Long generate(Long id)
{
if (id != null && id.longValue() != 0) {
return id;
}
return new Long(_idGen.get());
}
}
private static class IdGeneratorIdAsset extends IdGenerator
{
private IdAssetGenerator _idGen;
private IdGeneratorIdAsset(EntityInfo,?> entity)
{
super(entity);
_idGen = new IdAssetGenerator(entity.node());
}
@Override
public IdAsset generate(IdAsset id)
{
if (id != null) {
return id;
}
return new IdAsset(_idGen.get());
}
}
private static class TableLiteral extends AnnotationLiteral implements Asset
{
private final String _name;
public TableLiteral(String table)
{
_name = table;
}
@Override
public String value()
{
return _name;
}
}
private static class ColumnLiteral extends AnnotationLiteral
implements ColumnVault
{
private final String _name;
public ColumnLiteral(String name)
{
_name = name;
}
@Override
public String name()
{
return _name;
}
}
static {
_idGenMap = new HashMap<>();
_idGenMap.put(Long.class, IdGeneratorLong::new);
_idGenMap.put(long.class, IdGeneratorLong::new);
_idGenMap.put(IdAsset.class, IdGeneratorIdAsset::new);
_primitiveWrappers.put(boolean.class, Boolean.class);
_primitiveWrappers.put(char.class, Character.class);
_primitiveWrappers.put(byte.class, Byte.class);
_primitiveWrappers.put(short.class, Short.class);
_primitiveWrappers.put(int.class, Integer.class);
_primitiveWrappers.put(long.class, Long.class);
_primitiveWrappers.put(float.class, Float.class);
_primitiveWrappers.put(double.class, Double.class);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy