Package com.googlecode.objectify.impl.translate

Source Code of com.googlecode.objectify.impl.translate.ClassTranslator

package com.googlecode.objectify.impl.translate;

import com.google.appengine.api.datastore.PropertyContainer;
import com.googlecode.objectify.annotation.Subclass;
import com.googlecode.objectify.impl.Path;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* <p>Some common code for Translators which know how to convert a POJO type into a PropertiesContainer.
* This might be polymorphic; we get polymorphism when @Subclasses are registered on this translator.</p>
*
* @author Jeff Schnitzer <jeff@infohazard.org>
*/
public class ClassTranslator<P> extends NullSafeTranslator<P, PropertyContainer>
{
  private static final Logger log = Logger.getLogger(ClassTranslator.class.getName());

  /** Name of the out-of-band discriminator property in a PropertyContainer */
  public static final String DISCRIMINATOR_PROPERTY = "^d";

  /** Name of the list property which will hold all indexed discriminator values */
  public static final String DISCRIMINATOR_INDEX_PROPERTY = "^i";

  /** The declared class we are responsible for. */
  private final Class<P> declaredClass;

  /** Lets us construct the initial objects */
  private final Creator<P> creator;

  /** Does the heavy lifting of copying properties */
  private final Populator<P> populator;

  /**
   * The discriminator for this subclass, or null for the base class.
   */
  private final String discriminator;

  /**
   * The discriminators that will be indexed for this subclass.  Empty for the base class or any
   * subclasses for which all discriminators are unindexed.
   */
  private final List<String> indexedDiscriminators = new ArrayList<>();

  /** Keyed by discriminator value, including alsoload discriminators */
  private Map<String, ClassTranslator<? extends P>> byDiscriminator = new HashMap<>();

  /** Keyed by Class, includes the base class */
  private Map<Class<? extends P>, ClassTranslator<? extends P>> byClass = new HashMap<>();

  /** */
  public ClassTranslator(Class<P> declaredClass, Path path, Creator<P> creator, Populator<P> populator) {
    if (log.isLoggable(Level.FINEST))
      log.finest("Creating class translator for " + declaredClass.getName() + " at path '"+ path + "'");

    this.declaredClass = declaredClass;
    this.creator = creator;
    this.populator = populator;

    Subclass sub = declaredClass.getAnnotation(Subclass.class);
    if (sub != null) {
      discriminator = (sub.name().length() > 0) ? sub.name() : declaredClass.getSimpleName();
      addIndexedDiscriminators(declaredClass);
    } else {
      discriminator = null;
    }
  }

  /**
   * @return the class we translate
   */
  public Class<P> getDeclaredClass() {
    return declaredClass;
  }

  /**
   * @return the discriminator for this class, or null if this is not a @Subclass
   */
  public String getDiscriminator() {
    return discriminator;
  }

  /**
   * Get the populator associated with this class.
   */
  public Populator<P> getPopulator() {
    return populator;
  }

  /**
   * Get the creator associated with this class.
   */
  public Creator<P> getCreator() { return creator; }

  /* */
  @Override
  public P loadSafe(PropertyContainer container, LoadContext ctx, Path path) throws SkipException {
    // check if we need to redirect to a different translator
    String containerDiscriminator = (String)container.getProperty(DISCRIMINATOR_PROPERTY);
    if (!Objects.equals(discriminator, containerDiscriminator)) {
      ClassTranslator<? extends P> translator = byDiscriminator.get(containerDiscriminator);
      if (translator == null)
        throw new IllegalStateException("Datastore object has discriminator value '" + containerDiscriminator + "' but no relevant @Subclass is registered");
      else
        return translator.load(container, ctx, path);
    } else {
      // This is a normal load
      P into = creator.load(container, ctx, path);

      populator.load(container, ctx, path, into);

      return into;
    }
  }

  /* */
  @Override
  public PropertyContainer saveSafe(P pojo, boolean index, SaveContext ctx, Path path) throws SkipException {
    // check if we need to redirect to a different translator
    if (pojo.getClass() != declaredClass) {
      // Sometimes generics are more of a hindrance than a help
      @SuppressWarnings("unchecked")
      ClassTranslator<P> translator = (ClassTranslator<P>)byClass.get(pojo.getClass());
      if (translator == null)
        throw new IllegalStateException("Class '" + pojo.getClass() + "' is not a registered @Subclass");
      else
        return translator.save(pojo, index, ctx, path);
    } else {
      // This is a normal save
      PropertyContainer into = creator.save(pojo, index, ctx, path);

      populator.save(pojo, index, ctx, path, into);

      if (discriminator != null) {
        into.setUnindexedProperty(DISCRIMINATOR_PROPERTY, discriminator);

        if (!indexedDiscriminators.isEmpty())
          into.setProperty(DISCRIMINATOR_INDEX_PROPERTY, indexedDiscriminators);
      }

      return into;
    }
  }

  /**
   * Recursively go through the class hierarchy adding any discriminators that are indexed
   */
  private void addIndexedDiscriminators(Class<?> clazz) {
    if (clazz == Object.class)
      return;

    this.addIndexedDiscriminators(clazz.getSuperclass());

    Subclass sub = clazz.getAnnotation(Subclass.class);
    if (sub != null && sub.index()) {
      String disc = (sub.name().length() > 0) ? sub.name() : clazz.getSimpleName();
      this.indexedDiscriminators.add(disc);
    }
  }

  /**
   * Register a subclass translator with this class translator. That way if we get called upon
   * to translate an instance of the subclass, we will forward to the correct translator.
   */
  public void registerSubclass(ClassTranslator<? extends P> translator) {
    byDiscriminator.put(translator.getDiscriminator(), translator);

    Subclass sub = translator.getDeclaredClass().getAnnotation(Subclass.class);
    for (String alsoLoad: sub.alsoLoad())
      byDiscriminator.put(alsoLoad, translator);

    byClass.put(translator.getDeclaredClass(), translator);
  }

}
TOP

Related Classes of com.googlecode.objectify.impl.translate.ClassTranslator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.