package com.dooapp.gaedo.finders.root;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.dooapp.gaedo.finders.FieldInformer;
import com.dooapp.gaedo.finders.FieldInformerAPI;
import com.dooapp.gaedo.finders.Informer;
import com.dooapp.gaedo.finders.QueryExpression;
import com.dooapp.gaedo.finders.expressions.EqualsExpression;
import com.dooapp.gaedo.properties.Property;
import com.dooapp.gaedo.properties.PropertyProvider;
public class ReflectionBackedInformer<DataType> implements Informer<DataType> {
/**
* Class used when a {@link ReflectionBackedInformer} is seen as a field of another object
* @author ndx
*
*/
public class AsFieldInformer implements Informer<DataType> {
/**
* Field used to see the containing {@link ReflectionBackedInformer}
*/
private Property field;
/**
* Stored parent path
*/
private List<Property> parentPath = Collections.emptyList();
public AsFieldInformer(Property field) {
this.field = field;
}
@Override
public FieldInformer get(String string, Collection<Property> propertyPath) {
FieldInformer returned = ReflectionBackedInformer.this.get(string);
if(returned instanceof FieldInformerAPI) {
// Improve path with this informer one
Collection<Property> newPath = new LinkedList<Property>(propertyPath);
newPath.add(field);
returned = ((FieldInformerAPI) returned).with(newPath);
}
return returned;
}
@Override
public FieldInformer get(String string) {
return get(string, parentPath);
}
@Override
public QueryExpression equalsTo(Object value) {
return new EqualsExpression(field, getFieldPath(), value);
}
@Override
public Informer asField(Property field) {
return ReflectionBackedInformer.this.asField(field);
}
@Override
public Property getField() {
return field;
}
@Override
public Collection<FieldInformer> getAllFieldInformers() {
return ReflectionBackedInformer.this.getAllFieldInformers();
}
@Override
public Collection<Property> getAllFields() {
return ReflectionBackedInformer.this.getAllFields();
}
@Override
public Iterable<Property> getFieldPath() {
List<Property> returned = new LinkedList<Property>(parentPath);
returned.add(field);
return returned;
}
@Override
public FieldInformer with(Collection<Property> propertyPath) {
return new AsFieldInformer(field);
}
/**
* @return
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + ((field == null) ? 0 : field.hashCode());
return result;
}
/**
* @param obj
* @return
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AsFieldInformer other = (AsFieldInformer) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (field == null) {
if (other.field != null)
return false;
} else if (!field.equals(other.field))
return false;
return true;
}
private ReflectionBackedInformer getOuterType() {
return ReflectionBackedInformer.this;
}
}
/**
* Informer for super class
*/
private Informer<? super DataType> parent;
/**
* Local class, used for getting fields and properties
*/
private Class<DataType> clazz;
private Map<Property, FieldInformer> fields = new HashMap<Property, FieldInformer>();
private PropertyProvider propertyProvider;
public ReflectionBackedInformer(Class<DataType> clazz,
ReflectionBackedInformerFactory reflectionBackedInformerFactory,
PropertyProvider provider) {
// Immediatly load parent infos
if(!clazz.isAssignableFrom(Object.class)) {
parent = reflectionBackedInformerFactory.get(clazz.getSuperclass());
}
// Now, get all fields
this.clazz = clazz;
this.propertyProvider = provider;
loadFieldsInformers(reflectionBackedInformerFactory);
}
/**
* Load all fields informers from the class fields
* @param reflectionBackedInformerFactory
*/
private void loadFieldsInformers(ReflectionBackedInformerFactory reflectionBackedInformerFactory) {
Property[] fieldsArray = propertyProvider.get(clazz);
for(Property f : fieldsArray) {
try {
fields.put(f, reflectionBackedInformerFactory.getInformerFor(f));
} catch(UnsupportedOperationException e) {
e.printStackTrace();
}
}
}
@Override
public FieldInformer get(String string, Collection<Property> propertyPath) {
FieldInformer returned = internalGet(string);
if(returned instanceof FieldInformerAPI) {
returned = ((FieldInformerAPI) returned).with(new LinkedList<Property>(propertyPath));
}
return returned;
}
@Override
public FieldInformer get(String string) {
return get(string, new LinkedList<Property>());
}
public FieldInformer internalGet(String string) {
for(Map.Entry<Property, FieldInformer> f : fields.entrySet()) {
if(f.getKey().getName().equals(string)) {
return f.getValue();
} else if((clazz.getSimpleName()+"."+f.getKey().getName()).equals(string)) {
return f.getValue();
}
}
if(parent!=null) {
return parent.get(string);
} else {
throw new NoSuchFieldInHierarchyException(string);
}
}
/**
* The equalsTo method, as implemented by {@link Informer}, checks that the informer reference is equals to the reference given.
* As a consequence, a null value is given for the field (which is an error since it does not allows model navigation)
*/
@Override
public QueryExpression equalsTo(Object value) {
return new EqualsExpression(null, new LinkedList<Property>(), value);
}
@Override
public Informer asField(Property field) {
return new AsFieldInformer(field);
}
/**
* There is no field associated with object informer. As a consequence, an exception is thrown
*/
@Override
public Property getField() {
throw new UnsupportedOperationException("No field can be associated to a ReflectionBackedInformer, which only describes a root object");
}
/**
* Get all fields of this object. This method creates a short lifetime collection containing all fields of this object (coming from this class and from superclass)
* @return
*/
public Collection<FieldInformer> getAllFieldInformers() {
Collection<FieldInformer> toReturn = new LinkedList<FieldInformer>();
if(parent!=null) {
toReturn.addAll(parent.getAllFieldInformers());
}
toReturn.addAll(fields.values());
return toReturn;
}
@Override
public Collection<Property> getAllFields() {
Collection<Property> toReturn = new LinkedList<Property>();
if(parent!=null) {
toReturn.addAll(parent.getAllFields());
}
toReturn.addAll(fields.keySet());
return toReturn;
}
@Override
public Iterable<Property> getFieldPath() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("method "+FieldInformer.class.getName()+"#getFieldPath has not yet been implemented AT ALL");
}
@Override
public FieldInformer with(Collection<Property> propertyPath) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("method "+FieldInformerAPI.class.getName()+"#use has not yet been implemented AT ALL");
}
/**
* @return
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((clazz == null) ? 0 : clazz.hashCode());
return result;
}
/**
* @param obj
* @return
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectionBackedInformer other = (ReflectionBackedInformer) obj;
if (clazz == null) {
if (other.clazz != null)
return false;
} else if (!clazz.getCanonicalName().equals(other.clazz.getCanonicalName()))
return false;
return true;
}
}