package com.googlecode.objectify.impl.save;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.appengine.api.datastore.Entity;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.annotation.Indexed;
import com.googlecode.objectify.annotation.Unindexed;
import com.googlecode.objectify.condition.Always;
import com.googlecode.objectify.impl.TypeUtils;
import com.googlecode.objectify.impl.TypeUtils.FieldMetadata;
/**
* <p>Save which discovers how to save a class, either root pojo or embedded.</p>
*/
public class ClassSaver implements Saver
{
/** Classes are composed of fields, each of which could be a LeafSaver or an EmbeddedArraySaver etc */
List<Saver> fieldSavers = new ArrayList<Saver>();
/** If this is non-null, it means we have a value that should override the current save mode */
Boolean indexed;
/**
* Creates a ClassSaver for a root entity pojo class. If nothing is specified otherwise, all
* fields default to indexed
*/
public ClassSaver(ObjectifyFactory factory, Class<?> rootClazz)
{
this(factory, null, rootClazz, false, false);
}
/**
* @param pathPrefix is the entity path to this class, ie "field1.field2" for an embedded field1 containing a field2
* of the type of this class. The root pathPrefix is null.
* @param clazz is the class we want to save.
* @param ignoreIndexingAnnotations will cause the saver to ignore the @Indexed or @Unindexed annotations on the class
* @param collectionize causes all leaf setters to create and append to a simple list of
* values rather than to set the value directly. After we hit an embedded array or
* an embedded collection, all subsequent savers are collectionized.
*/
public ClassSaver(ObjectifyFactory factory, String pathPrefix, Class<?> clazz, boolean ignoreIndexingAnnotations, boolean collectionize)
{
Indexed indexedAnn = clazz.getAnnotation(Indexed.class);
Unindexed unindexedAnn = clazz.getAnnotation(Unindexed.class);
if (indexedAnn != null && unindexedAnn != null)
throw new IllegalStateException("Cannot have @Indexed and @Unindexed on the same class: " + clazz.getName());
if (indexedAnn != null && (indexedAnn.value().length != 1 || indexedAnn.value()[0] != Always.class)
|| unindexedAnn != null && (unindexedAnn.value().length != 1 || unindexedAnn.value()[0] != Always.class))
throw new IllegalStateException("Class-level @Indexed and @Unindexed annotations cannot have If conditions: " + clazz.getName());
// If we aren't ignoring our annotations we're an embedded class and the
// field had an indexing annotation, we can look at our own indexing annotations.
if (!ignoreIndexingAnnotations)
{
if (indexedAnn != null)
this.indexed = true;
else if (unindexedAnn != null)
this.indexed = false;
}
List<FieldMetadata> fields = TypeUtils.getPesistentFields(clazz);
for (FieldMetadata metadata: fields)
{
Field field = metadata.field;
if (TypeUtils.isEmbedded(field))
{
if (field.getType().isArray())
{
Saver saver = new EmbeddedArrayFieldSaver(factory, pathPrefix, clazz, field, collectionize);
this.fieldSavers.add(saver);
}
else if (Collection.class.isAssignableFrom(field.getType()))
{
Saver saver = new EmbeddedCollectionFieldSaver(factory, pathPrefix, clazz, field, collectionize);
this.fieldSavers.add(saver);
}
else // basic class
{
Saver saver = new EmbeddedClassFieldSaver(factory, pathPrefix, clazz, field, collectionize);
this.fieldSavers.add(saver);
}
}
else // not embedded, so we're at a leaf object (including arrays and collections of basic types)
{
// Add a leaf saver
Saver saver = new LeafFieldSaver(factory, pathPrefix, clazz, field, collectionize);
this.fieldSavers.add(saver);
}
}
}
/* (non-Javadoc)
* @see com.googlecode.objectify.impl.Saver#save(java.lang.Object, com.google.appengine.api.datastore.Entity)
*/
@Override
public void save(Object pojo, Entity entity, boolean index)
{
if (this.indexed != null)
index = this.indexed;
for (Saver fieldSaver: this.fieldSavers)
fieldSaver.save(pojo, entity, index);
}
}