package io.cattle.platform.api.resource.jooq;
import io.cattle.platform.api.utils.ApiUtils;
import io.cattle.platform.object.meta.ObjectMetaDataManager;
import io.cattle.platform.object.util.ObjectUtils;
import io.github.ibuildthecloud.gdapi.model.Pagination;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.AbstractSequentialList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.RecordHandler;
import org.jooq.Table;
public class MultiTableMapper extends AbstractSequentialList<Object> implements RecordHandler<Record> {
ObjectMetaDataManager metaDataManager;
List<Table<?>> tables = new ArrayList<Table<?>>();
Map<String, TableMapping> mappings = new HashMap<String, MultiTableMapper.TableMapping>();
Map<String, TableMapping> fieldsMapping = new HashMap<String, TableMapping>();
List<Field<?>> fields = new ArrayList<Field<?>>();
Map<Object, Object> result = new LinkedHashMap<Object, Object>();
int resultSize;
Pagination pagination;
Integer limit;
public MultiTableMapper(ObjectMetaDataManager metaDataManager, Pagination pagination) {
this.metaDataManager = metaDataManager;
this.pagination = pagination;
limit = pagination == null ? null : pagination.getLimit();
}
public MultiTableMapper map(Table<?> table) {
return map("", table);
}
public MultiTableMapper map(String name, Table<?> table) {
boolean emptyPrefix = name.equals("");
TableMapping mapping = new TableMapping();
String prefix = emptyPrefix ? "" : "table" + mappings.size() + "_";
mapping.originalTable = table;
mapping.keyName = name;
mapping.prefix = prefix;
mapping.aliasedTable = table.as(mapping.prefix + table.getName());
mapping.originalFields = table.fields();
mapping.aliasedFields = new Field<?>[mapping.originalFields.length];
Field<?>[] unaliasedFields = mapping.aliasedTable.fields();
for (int i = 0; i < mapping.aliasedFields.length; i++) {
Field<?> field = unaliasedFields[i];
Field<?> alias = emptyPrefix ? field : field.as(prefix + field.getName());
mapping.aliasedFields[i] = alias;
fieldsMapping.put(alias.getName(), mapping);
fields.add(alias);
}
tables.add(mapping.aliasedTable);
mappings.put(mapping.keyName, mapping);
return this;
}
public List<Table<?>> getTables() {
return tables;
}
public List<Field<?>> getFields() {
return fields;
}
@Override
public void next(Record record) {
resultSize++;
if (limit != null && resultSize > limit) {
return;
}
Map<String, Object> objects = new HashMap<String, Object>();
for (Field<?> field : record.fields()) {
TableMapping mapping = fieldsMapping.get(field.getName());
if (mapping == null) {
continue;
}
Object obj = objects.get(mapping.keyName);
if (obj == null) {
obj = newObject(mapping);
objects.put(mapping.keyName, obj);
}
String fieldName = StringUtils.removeStart(field.getName(), mapping.prefix);
String propertyName = metaDataManager.lookupPropertyNameFromFieldName(mapping.originalTable.getRecordType(), fieldName);
if (propertyName != null) {
setProperty(obj, propertyName, record, field);
}
}
Object rootObject = objects.remove("");
Object id = ObjectUtils.getId(rootObject);
String key = ApiUtils.getAttachementKey(rootObject, id);
if (key == null) {
return;
}
result.put(id, rootObject);
for (Map.Entry<String, Object> entry : objects.entrySet()) {
ApiUtils.addAttachement(key, entry.getKey(), entry.getValue());
}
}
protected void setProperty(Object obj, String prop, Record record, Field<?> field) {
try {
PropertyDescriptor desc = PropertyUtils.getPropertyDescriptor(obj, prop);
if (desc != null && desc.getWriteMethod() != null) {
Object value = record.getValue(field, desc.getWriteMethod().getParameterTypes()[0]);
PropertyUtils.setProperty(obj, prop, value);
}
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Failed to set property [" + prop + "] on [" + obj + "]", e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException("Failed to set property [" + prop + "] on [" + obj + "]", e);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Failed to set property [" + prop + "] on [" + obj + "]", e);
}
}
protected Object newObject(TableMapping mapping) {
Class<?> clz = mapping.originalTable.getRecordType();
try {
return clz.newInstance();
} catch (InstantiationException e) {
throw new IllegalStateException("Failed to construct [" + clz + "]", e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Failed to construct [" + clz + "]", e);
}
}
private static class TableMapping {
String keyName;
Table<?> originalTable;
Table<?> aliasedTable;
String prefix;
Field<?>[] originalFields;
Field<?>[] aliasedFields;
}
@Override
public ListIterator<Object> listIterator(int index) {
return new ForwardListIterator(index, result.values().iterator());
}
@Override
public int size() {
return result.size();
}
public int getResultSize() {
return resultSize;
}
}