package com.alvazan.orm.api.z8spi.meta;
import java.util.ArrayList;
import java.util.Collection;
import com.alvazan.orm.api.base.anno.NoSqlDiscriminatorColumn;
import com.alvazan.orm.api.base.anno.NoSqlManyToOne;
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;
@SuppressWarnings("rawtypes")
@NoSqlDiscriminatorColumn(value="listOfFk")
public class DboColumnToManyMeta extends DboColumnMeta {
/**
* This field may be referencing another entity in another table so here is the meta data
* on that table as well, but for now, I don't think we need it until we have joins
*/
@NoSqlManyToOne
private DboTableMeta fkToColumnFamily;
/**
* When someone in an entity uses CursorToMany, it is because there are too many and could blow out the memory so
* they use CursorToMany and we store the tomany in a separate index row so they can cursor over the results.
*/
private Boolean isSeparateRow;
public void setup(DboTableMeta owner, String colName, DboTableMeta fkToTable, boolean isCursor) {
super.setup(owner, colName, false, false);
this.fkToColumnFamily = fkToTable;
this.isSeparateRow = isCursor;
}
@Override
public String getIndexTableName() {
DboColumnIdMeta idMeta = fkToColumnFamily.getIdColumnMeta();
StorageTypeEnum storageType = idMeta.getStorageType();
return storageType.getIndexTableName();
}
public DboTableMeta getFkToColumnFamily() {
return fkToColumnFamily;
}
public boolean isSeparateRow() {
if(isSeparateRow == null)
return false;
return isSeparateRow;
}
@Override
public Class getClassType() {
throw new UnsupportedOperationException("Need to figure out how to convert Class to a type of Array Class");
// Class typeInTheArray = fkToColumnFamily.getIdColumnMeta().getClassType();
}
@Override
public StorageTypeEnum getStorageType() {
StorageTypeEnum typeInTheArray = fkToColumnFamily.getIdColumnMeta().getStorageType();
return typeInTheArray;
}
@Override
public void translateFromColumn(Row row, TypedRow entity) {
translateFromColumnList(row, entity);
}
private void translateFromColumnList(Row row, TypedRow entity) {
parseOutKeyList(row, entity);
}
private void parseOutKeyList(Row row, TypedRow entity) {
String columnName = getColumnName();
byte[] namePrefix = StandardConverters.convertToBytes(columnName);
Collection<Column> columns = row.columnByPrefix(namePrefix);
//NOTE: Current implementation is just like a Set not a List in that it
//cannot have repeats right now. We could take the approach the column name
//would be <prefix><index><pk> such that duplicates are allowed and when loaded
//we would have to keep the index in the proxy so if removed, we could put the col name
//back together so we could remove it and add the new one. This is very complex though when
//it comes to removing one item and shifting all other column names by one(ie. lots of removes/adds)
//so for now, just make everything Set like.
for(Column col : columns) {
byte[] fullName = col.getName();
//strip off the prefix to get the foreign key
int pkLen = fullName.length-namePrefix.length;
byte[] fk = new byte[pkLen];
for(int i = namePrefix.length; i < fullName.length; i++) {
fk[i-namePrefix.length] = fullName[i];
}
entity.addColumn(this, fullName, namePrefix, fk, col.getValue(), col.getTimestamp());
}
}
@Override
public void translateToColumn(InfoForIndex<TypedRow> info) {
TypedRow entity = info.getEntity();
RowToPersist row = info.getRow();
translateToColumnList(entity, row);
}
@SuppressWarnings({ "unchecked" })
private void translateToColumnList(TypedRow entity, RowToPersist row) {
TypedColumn column = entity.getColumn(getColumnName());
if (column == null)
return;
Object valueObj = column.getValue();
if(!(valueObj instanceof Collection))
throw new IllegalArgumentException("For column family="+this.owner.getColumnFamily()+" you passed in a row and column value for column="+getColumnName()+" must inherit from Collection and does not(for this column as it is a ToMany column)");
Collection fks = (Collection) valueObj;
Collection<Object> toBeAdded = fks; //all values in the list get added if not an OurAbstractCollection
Collection<Object> toBeRemoved = new ArrayList<Object>();
throw new UnsupportedOperationException("not complete yet, need to add the below code but use a more simple list");
// if(fks instanceof OurAbstractCollection) {
// OurAbstractCollection<Object> coll = (OurAbstractCollection<Object>)fks;
// toBeRemoved = coll.getToBeRemoved();
// toBeAdded = coll.getToBeAdded();
// }
//
// translateToColumnImpl(toBeAdded, row, toBeRemoved);
}
private void translateToColumnImpl(Collection<Object> toBeAdded, RowToPersist row, Collection<Object> toBeRemoved) {
//removes first
for(Object theFk : toBeRemoved) {
byte[] name = formTheNameImpl(theFk);
row.addEntityToRemove(name);
}
//now process all the existing columns (we can add same entity as many times as we like and it does not
//get duplicated)
for(Object fk : toBeAdded) {
byte[] name = formTheNameImpl(fk);
Column c = new Column();
c.setName(name);
row.getColumns().add(c);
}
}
private byte[] formTheNameImpl(Object p) {
byte[] byteVal = converter.convertToNoSql(p);
byte[] pkData = byteVal;
byte[] prefix = getColumnNameAsBytes();
byte[] name = new byte[prefix.length + pkData.length];
for(int i = 0; i < name.length; i++) {
if(i < prefix.length)
name[i] = prefix[i];
else
name[i] = pkData[i-prefix.length];
}
return name;
}
@Override
public boolean isPartitionedByThisColumn() {
return false;
}
@Override
public String fetchColumnValueAsString(TypedRow row) {
throw new UnsupportedOperationException("only used by partitioning and can't partition a multe value column....that is kind of useless");
}
}