package org.deephacks.confit.internal.cached.query;
import com.googlecode.cqengine.attribute.Attribute;
import com.googlecode.cqengine.attribute.MultiValueNullableAttribute;
import com.googlecode.cqengine.attribute.SimpleNullableAttribute;
import org.deephacks.confit.Index;
import org.deephacks.confit.model.Schema;
import org.deephacks.confit.model.Schema.AbstractSchemaProperty;
import org.deephacks.confit.model.Schema.SchemaProperty;
import org.deephacks.confit.model.Schema.SchemaPropertyList;
import org.deephacks.confit.model.Schema.SchemaPropertyRef;
import org.deephacks.confit.model.Schema.SchemaPropertyRefList;
import org.deephacks.confit.model.Schema.SchemaPropertyRefMap;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ConfigIndex {
private Class<?> cls;
private HashMap<String, Attribute<?, ?>> attributes = new HashMap<>();
public ConfigIndex(Schema schema) {
for(AbstractSchemaProperty property : schema.getIndexed()) {
String propertyName = property.getName();
Class<?> type = getType(property);
if(Number.class.isAssignableFrom(type) || isPrimitiveNumber(type)) {
NumberAttribute attr = new NumberAttribute(propertyName);
attributes.put(propertyName, attr);
} else if (Collection.class.isAssignableFrom(type)) {
MultiObjectAttribute attr = new MultiObjectAttribute(propertyName);
attributes.put(propertyName, attr);
} else if (Map.class.isAssignableFrom(type)) {
MultiObjectAttribute attr = new MultiObjectAttribute(propertyName);
attributes.put(propertyName, attr);
} else {
ObjectAttribute attr = new ObjectAttribute(propertyName);
attributes.put(propertyName, attr);
}
}
}
private Class<?> getType(final AbstractSchemaProperty property) {
if (property instanceof SchemaProperty) {
return ((SchemaProperty) property).getClassType();
} else if (property instanceof SchemaPropertyList) {
return ((SchemaPropertyList) property).getClassCollectionType();
} else if (property instanceof SchemaPropertyRef) {
return String.class;
} else if (property instanceof SchemaPropertyRefList ||
property instanceof SchemaPropertyRefMap) {
return ArrayList.class;
} else {
throw new IllegalArgumentException("Unrecognized property");
}
}
public Attribute get(String field) {
return attributes.get(field);
}
public List<Attribute> get() {
List<Attribute> attrs = new ArrayList<>();
for(Attribute att : attributes.values()){
attrs.add(att);
}
return attrs;
}
private List<Field> getIndexedFields(Class<?> cls) {
List<Field> fields = new ArrayList<>();
for(final Field field : cls.getDeclaredFields()) {
field.setAccessible(true);
if(field.getAnnotation(Index.class) != null) {
fields.add(field);
}
}
return fields;
}
static final class ObjectAttribute extends SimpleNullableAttribute<ConfigIndexFields, Object> {
private final String field;
public ObjectAttribute(String field) {
this.field = field;
}
@Override
public Object getValue(ConfigIndexFields data) {
return data.fields.get(field);
}
}
static final class MultiObjectAttribute extends MultiValueNullableAttribute<ConfigIndexFields, Object> {
private final String field;
public MultiObjectAttribute(String field) {
super(field, false);
this.field = field;
}
@Override
public List<Object> getNullableValues(ConfigIndexFields data) {
Object o = data.fields.get(field);
if(o == null) {
return null;
}
Class<?> cls = o.getClass();
if(Collection.class.isAssignableFrom(cls)) {
return (List<Object>) o;
} else if(Map.class.isAssignableFrom(cls)) {
ArrayList<Object> values = new ArrayList<>();
for(Object value : ((Map)o).values()) {
values.add(value);
}
return values;
} else {
throw new IllegalArgumentException("Could not lookup correct collection from ["+o+"]");
}
}
}
static final class NumberAttribute extends SimpleNullableAttribute<ConfigIndexFields, Number> {
private final String field;
public NumberAttribute(String field) {
this.field = field;
}
@Override
public Number getValue(ConfigIndexFields data) {
return (Number) data.fields.get(field);
}
}
private static final Map<String, Class<?>> ALL_PRIMITIVE_NUMBERS = new HashMap<>();
static {
for (Class<?> primitiveNumber : Arrays.asList(byte.class, short.class,
int.class, long.class, float.class, double.class)) {
ALL_PRIMITIVE_NUMBERS.put(primitiveNumber.getName(), primitiveNumber);
}
}
public static boolean isPrimitiveNumber(Class<?> type) {
return ALL_PRIMITIVE_NUMBERS.get(type.getName()) != null;
}
}