package com.alvazan.orm.impl.meta.data; import java.lang.reflect.Field; import java.util.Collection; import java.util.List; import com.alvazan.orm.api.base.ToOneProvider; import com.alvazan.orm.api.exc.ChildWithNoPkException; import com.alvazan.orm.api.z5api.NoSqlSession; import com.alvazan.orm.api.z8spi.Row; import com.alvazan.orm.api.z8spi.action.Column; import com.alvazan.orm.api.z8spi.conv.StandardConverters; import com.alvazan.orm.api.z8spi.conv.StorageTypeEnum; import com.alvazan.orm.api.z8spi.meta.DboColumnMeta; import com.alvazan.orm.api.z8spi.meta.DboColumnToOneMeta; import com.alvazan.orm.api.z8spi.meta.DboTableMeta; import com.alvazan.orm.api.z8spi.meta.IndexData; import com.alvazan.orm.api.z8spi.meta.InfoForIndex; import com.alvazan.orm.api.z8spi.meta.ReflectionUtil; import com.alvazan.orm.api.z8spi.meta.RowToPersist; import com.alvazan.orm.impl.meta.data.collections.ToOneProviderProxy; public class MetaProxyField<OWNER, PROXY> extends MetaAbstractField<OWNER> { //ClassMeta Will eventually have the idField that has the converter!!! //once it is scanned private MetaAbstractClass<PROXY> classMeta; private DboColumnToOneMeta metaDbo = new DboColumnToOneMeta(); public DboColumnMeta getMetaDbo() { return metaDbo; } @Override public String toString() { return "MetaProxyField [field='" + field.getDeclaringClass().getName()+"."+field.getName()+"(field type=" +field.getType().getName()+ "), columnName=" + columnName + "]"; } public void translateFromColumn(Row row, OWNER entity, NoSqlSession session) { String columnName = getColumnName(); byte[] colBytes = StandardConverters.convertToBytes(columnName); Column column = row.getColumn(colBytes); if(column == null) { column = new Column(); } Object proxy; if(field.getType().equals(ToOneProvider.class)) { proxy = translateFromToComposite(row, session); if (proxy == null) proxy = translateFromToProxy(row, column.getValue(), session); } else { proxy = convertIdToProxyComposite(row, session); if (proxy == null) proxy = convertIdToProxy(row, column.getValue(), session); } ReflectionUtil.putFieldValue(entity, field, proxy); } private Object translateFromToProxy(Row row, byte[] value, NoSqlSession session) { ToOneProvider<PROXY> toOne = new ToOneProviderProxy(classMeta, value, session); return toOne; } private Object translateFromToComposite(Row row, NoSqlSession session) { byte[] bytes = StandardConverters.convertToBytes(columnName); Collection<Column> columns = row.columnByPrefix(bytes); if (columns != null && !columns.isEmpty()) { Column column = columns.iterator().next(); byte[] fullName = column.getName(); //strip off the prefix to get the foreign key int pkLen = fullName.length-bytes.length; byte[] fk = new byte[pkLen]; for(int i = bytes.length; i < fullName.length; i++) { fk[i-bytes.length] = fullName[i]; } ToOneProvider<PROXY> toOne = new ToOneProviderProxy(classMeta, fk, session); return toOne; } else return null; } private Object convertIdToProxyComposite(Row row, NoSqlSession session) { byte[] bytes = StandardConverters.convertToBytes(columnName); Collection<Column> columns = row.columnByPrefix(bytes); if (columns != null && !columns.isEmpty()) { Column column = columns.iterator().next(); byte[] fullName = column.getName(); //strip off the prefix to get the foreign key int pkLen = fullName.length-bytes.length; byte[] fk = new byte[pkLen]; for(int i = bytes.length; i < fullName.length; i++) { fk[i-bytes.length] = fullName[i]; } Tuple<PROXY> tuple = classMeta.convertIdToProxy(row, session, fk, null); return tuple.getProxy(); } else return null; } @SuppressWarnings("unchecked") public void translateToColumn(InfoForIndex<OWNER> info) { OWNER entity = info.getEntity(); RowToPersist row = info.getRow(); Column col = new Column(); row.getColumns().add(col); PROXY value = (PROXY) ReflectionUtil.fetchFieldValue(entity, field); if(value instanceof ToOneProvider) { value = (PROXY) ((ToOneProvider)value).get(); } //Value is the Account.java or a Proxy of Account.java field and what we need to save in //the database is the ID inside this Account.java object!!!! byte[] byteVal = classMeta.convertEntityToId(value); if(byteVal == null && value != null) { //if value is not null but we get back a byteVal of null, it means the entity has not been //initialized with a key yet, BUT this is required to be able to save this object String owner = "'"+field.getDeclaringClass().getSimpleName()+"'"; String child = "'"+field.getType().getSimpleName()+"'"; String fieldName = "'"+field.getType().getSimpleName()+" "+field.getName()+"'"; throw new ChildWithNoPkException("The entity you are saving of type="+owner+" has a field="+fieldName +" that does not yet have a primary key so you cannot save it. To correct this\n" + "problem, you can either\n" +"1. SAVE the "+child+" BEFORE you save the "+owner+" OR\n" +"2. Call entityManager.fillInWithKey(Object entity), then SAVE your "+owner+"', then save your "+child+" NOTE that this" + "\nmethod #2 is used for when you have a bi-directional relationship where each is a child of the other"); } byte[] colBytes = StandardConverters.convertToBytes(columnName); if(byteVal != null) { byte[] name = new byte[colBytes.length + byteVal.length]; for(int i = 0; i < name.length; i++) { if(i < colBytes.length) name[i] = colBytes[i]; else name[i] = byteVal[i-colBytes.length]; } col.setName(name); StorageTypeEnum storageType = getStorageType(); Object primaryKey = classMeta.fetchId(value); addIndexInfo(info, primaryKey, byteVal, storageType); removeIndexInfo(info, primaryKey, byteVal, storageType); } else { col.setName(colBytes); col.setValue(byteVal); StorageTypeEnum storageType = getStorageType(); Object primaryKey = classMeta.fetchId(value); addIndexInfo(info, primaryKey, byteVal, storageType); removeIndexInfo(info, primaryKey, byteVal, storageType); } } @SuppressWarnings("unchecked") @Override public Object fetchField(Object entity) { PROXY value = (PROXY) ReflectionUtil.fetchFieldValue(entity, field); return value; //throw new UnsupportedOperationException("only used for partitioning and multivalue column can't partition. easy to implement if anyone else starts using this though, but for now unsupported"); } @SuppressWarnings("unchecked") @Override public String translateToString(Object fieldsValue) { Object id = classMeta.fetchId((PROXY) fieldsValue); return classMeta.getIdField().getConverter().convertTypeToString(id); } private StorageTypeEnum getStorageType() { StorageTypeEnum storageType = classMeta.getIdField().getMetaIdDbo().getStorageType(); return storageType; } @Override public void removingEntity(InfoForIndex<OWNER> info, List<IndexData> indexRemoves, byte[] pk) { removingThisEntity(info, indexRemoves, pk); } @SuppressWarnings("unchecked") @Override public byte[] translateValue(Object value) { byte[] pk = classMeta.convertEntityToId((PROXY) value); if(pk == null && value != null) { throw new ChildWithNoPkException("You can't give us an entity with no pk!!!! We use the pk to search the database index. Please fix your bug"); } return pk; } public PROXY convertIdToProxy(Row row, byte[] nonVirtFk, NoSqlSession session) { Tuple<PROXY> tuple = classMeta.convertIdToProxy(row, session, nonVirtFk, null); return tuple.getProxy(); } public void setup(DboTableMeta tableMeta, Field field2, String colName, MetaAbstractClass<PROXY> classMeta, boolean isIndexed, boolean isPartitionedBy) { DboTableMeta fkToTable = classMeta.getMetaDbo(); metaDbo.setup(tableMeta, colName, fkToTable, isIndexed, isPartitionedBy); super.setup(field2, colName); this.classMeta = classMeta; } @SuppressWarnings("unchecked") @Override protected Object unwrapIfNeeded(Object value) { PROXY proxy = (PROXY) value; return classMeta.fetchId(proxy); } }