package com.googlecode.objectify.impl; import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.annotation.IgnoreSave; import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Unindex; import com.googlecode.objectify.condition.If; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; import java.lang.reflect.Type; /** * Property which encapsulates a simple field. */ public class FieldProperty extends AbstractProperty { Field field; MethodHandle getter; MethodHandle setter; /** These are authoritative */ If<?, ?>[] indexConditions; If<?, ?>[] unindexConditions; If<?, ?>[] ignoreSaveConditions; /** If we have an @IgnoreSave and it isn't Always */ boolean hasIgnoreSaveConditions; /** * @param examinedClass is the actual top level concrete class we are examining; the field might * be declared on a superclass of this class so it's not the same as field.getDeclaringClass() */ public FieldProperty(ObjectifyFactory fact, Class<?> examinedClass, Field field) { super(field.getName(), field.getAnnotations(), field); field.setAccessible(true); this.field = field; try { this.getter = MethodHandles.lookup().unreflectGetter(field); this.setter = MethodHandles.lookup().unreflectSetter(field); } catch (ReflectiveOperationException e) { throw new IllegalStateException(e); } IfConditionGenerator ifGenerator = new IfConditionGenerator(fact); // Check @Index and @Unindex conditions Index indexedAnn = field.getAnnotation(Index.class); Unindex unindexedAnn = field.getAnnotation(Unindex.class); if (indexedAnn != null && unindexedAnn != null) throw new IllegalStateException("Cannot have @Indexed and @Unindexed on the same field: " + field); if (indexedAnn != null) this.indexConditions = ifGenerator.generateIfConditions(indexedAnn.value(), field); if (unindexedAnn != null) this.unindexConditions = ifGenerator.generateIfConditions(unindexedAnn.value(), field); // Now watch out for @IgnoreSave conditions IgnoreSave ignoreSave = field.getAnnotation(IgnoreSave.class); if (ignoreSave != null) ignoreSaveConditions = ifGenerator.generateIfConditions(ignoreSave.value(), field); } /** */ @Override public Type getType() { return this.field.getGenericType(); } /** */ @Override public void set(Object pojo, Object value) { try { //this.field.set(pojo, value); setter.invoke(pojo, value); } catch (RuntimeException ex) { throw ex; } catch (Throwable ex) { throw new RuntimeException(ex); } } /** */ @Override public Object get(Object pojo) { try { //return this.field.get(pojo); return getter.invoke(pojo); } catch (RuntimeException ex) { throw ex; } catch (Throwable ex) { throw new RuntimeException(ex); } } /** */ @Override public String toString() { return this.field.toString(); } /** */ @Override public boolean isSaved(Object onPojo) { return !this.matches(onPojo, ignoreSaveConditions); } /** */ @Override public Boolean getIndexInstruction(Object onPojo) { if (this.matches(onPojo, indexConditions)) return true; else if (this.matches(onPojo, unindexConditions)) return false; else return null; } /** * Tests whether a set of conditions match. * @param conditions can be null; this always matches false * @return true if we match the conditions, false if we do not */ private boolean matches(Object onPojo, If<?, ?>[] conditions) { if (conditions == null) return false; Object value = this.get(onPojo); for (If<?, ?> condition: conditions) { @SuppressWarnings("unchecked") If<Object, Object> cond = (If<Object, Object>)condition; if (cond.matchesValue(value)) return true; if (cond.matchesPojo(onPojo)) return true; } return false; } }