Package me.prettyprint.hom

Source Code of me.prettyprint.hom.ClassCacheMgr

package me.prettyprint.hom;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;


import me.prettyprint.hom.converters.Converter;
import me.prettyprint.hom.converters.DefaultConverter;


/**
* Manage parsing and caching of class meta-data.
*
* @author
*/
public class ClassCacheMgr {
  private Map<String, CFMappingDef<?, ?>> cfMapByColFamName = new HashMap<String, CFMappingDef<?, ?>>();
  private Map<Class<?>, CFMappingDef<?, ?>> cfMapByClazz = new HashMap<Class<?>, CFMappingDef<?, ?>>();

  /**
   * Retrieve class meta-data by <code>class</code> object.
   *
   * @param <T>
   * @param <I>
   * @param clazz
   * @param throwException
   * @return
   */
  public <T, I> CFMappingDef<T, I> getCfMapDef(Class<T> clazz, boolean throwException) {
    @SuppressWarnings("unchecked")
    CFMappingDef<T, I> cfMapDef = (CFMappingDef<T, I>) cfMapByClazz.get(clazz);
    if (null == cfMapDef && throwException) {
      throw new IllegalStateException(
          "could not find property definitions for class, "
          + clazz.getSimpleName()
          + ", in class cache.  This indicates the EntityManager was not initialized properly.  If not using EntityManager the cache must be explicity initialized");

    }
    return cfMapDef;
  }

  /**
   * Retrieve class meta-data by column family name.
   *
   * @param <T>
   * @param <I>
   * @param colFamName
   * @param throwException
   * @return
   */
  public <T, I> CFMappingDef<T, I> getCfMapDef(String colFamName, boolean throwException) {
    @SuppressWarnings("unchecked")
    CFMappingDef<T, I> cfMapDef = (CFMappingDef<T, I>) cfMapByColFamName.get(colFamName);
    if (null == cfMapDef && throwException) {
      throw new IllegalStateException(
          "could not find property definitions for column family, "
          + colFamName
          + ", in class cache.  This indicates the EntityManager was not initialized properly.  If not using EntityManager the cache must be explicity initialized");

    }
    return cfMapDef;
  }

  /**
   * For each class that should be managed, this method must be called to
   * parse its annotations and derive its meta-data.
   *
   * @param clazz
   * @return
   */
  public <T, I> CFMappingDef<T, I> initializeCacheForClass(Class<T> clazz) {
    CFMappingDef<T, I> cfMapDef = initializeColumnFamilyMapDef(clazz);
    try {
      initializePropertiesMapDef(cfMapDef);
    }
    catch (IntrospectionException e) {
      throw new RuntimeException(e);
    }
    catch (InstantiationException e) {
      throw new RuntimeException(e);
    }
    catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    }
    return cfMapDef;
  }

  private <T, I> void initializePropertiesMapDef(CFMappingDef<T, I> cfMapDef) throws IntrospectionException,
  InstantiationException, IllegalAccessException {
    // if no property descriptors then return leaving empty annotation map
    PropertyDescriptor[] pdArr =
      Introspector.getBeanInfo(cfMapDef.getClazz(), cfMapDef.getClazz().getSuperclass())
      .getPropertyDescriptors();
    if (null == pdArr || 0 == pdArr.length && null == cfMapDef.getCfBaseMapDef()) {
      throw new IllegalStateException("could not find any properties annotated with @"
          + Column.class.getSimpleName());
    }

    // create tmp map for easy field -> descriptor mapping
    Map<String, PropertyDescriptor> pdMap = new HashMap<String, PropertyDescriptor>();
    for (PropertyDescriptor pd : pdArr) {
      pdMap.put(pd.getName(), pd);
    }

    Class<T> theType = cfMapDef.getClazz();
    // do {
    Field[] fieldArr = theType.getDeclaredFields();
    for (Field f : fieldArr) {
      Column colAnno = f.getAnnotation(javax.persistence.Column.class);
      if (null != colAnno) {
        PropertyDescriptor pd = pdMap.get(f.getName());
        if (null == pd) {
          throw new IllegalStateException("Property, " + cfMapDef.getClazz().getSimpleName() + "."
              + f.getName() + ", does not have proper setter/getter");
        }

        @SuppressWarnings("unchecked")
        PropertyMappingDefinition md =
          new PropertyMappingDefinition(pd, colAnno.name(), DefaultConverter.class);
        cfMapDef.addPropertyDefinition(md);
      }

      // TODO lookup JPA 2 spec for class-level ids
      Id idAnno = f.getAnnotation(Id.class);
      if (null != idAnno) {
        @SuppressWarnings("unchecked")
        PropertyMappingDefinition md =
          new PropertyMappingDefinition(pdMap.get(f.getName()), null, DefaultConverter.class);
        if (null != md) {
          if (null == md.getPropDesc() || null == md.getPropDesc().getReadMethod()
              || null == md.getPropDesc().getWriteMethod()) {
            throw new IllegalStateException("@" + Id.class.getSimpleName() + " is defined on property, "
                + f.getName() + ", but its missing proper setter/getter");
          }
        }
        cfMapDef.setIdPropertyMap(md);
      }
    }

    // theType = (Class<T>) theType.getSuperclass();
    // } while (Object.class != theType);
  }

  private <T, I> CFMappingDef<T, I> initializeColumnFamilyMapDef(Class<T> clazz) {
    // if already init'd don't do it again - could have happened because of
    // inheritance
    CFMappingDef<T, I> cfMapDef = getCfMapDef(clazz, false);
    if (null != cfMapDef) {
      return cfMapDef;
    }

    // if anonymous class, then get the superclass (the real class)
    Class<T> theType = clazz;
    boolean entityFound;
    while (!(entityFound = (null != theType.getAnnotation(Entity.class)))) {
      if (!theType.isAnonymousClass()) {
        break;
      }
      else {
        theType = (Class<T>) theType.getSuperclass();
      }
    }

    // if class is missing @Entity, then proceed no further
    if (!entityFound) {
      throw new IllegalStateException("class, " + clazz.getName() + ", not annotated with @"
          + Entity.class.getSimpleName());
    }

    cfMapDef = new CFMappingDef<T, I>(theType);
    CFMappingDef<? super T, I> cfBaseMapDef = null;

    // if this class extends a super, then process it first
    if (null != theType.getSuperclass()) {
      try {
        cfBaseMapDef = initializeCacheForClass(theType.getSuperclass());
        cfMapDef.setCfSuperMapDef((CFMappingDef<? super T, I>) getCfMapDef(theType.getSuperclass(), false));
      }
      catch (IllegalStateException e) {
        // this is ok, super class might not be an enitity
      }
    }

    // determine column family name
    Table tbl = theType.getAnnotation(Table.class);
    if (null == cfBaseMapDef) {
      if (null != tbl) {
        // make sure the column family is not already used
        CFMappingDef<?, ?> tmpDef;
        if ( null != (tmpDef=getCfMapDef(tbl.name(), false))) {
          throw new IllegalStateException("classes, " + clazz.getName() + " and " + tmpDef.getClazz().getName() + ", are both mapped to ColumnFamily, " + tbl.name() + ".  Can only have one class/ColumnFamily mapping - if multiple classes can be derived from a single ColumnFamily, use @"+Inheritance.class.getSimpleName() );
        }
        cfMapDef.setColFamName(tbl.name());
      }
      else {
        cfMapDef.setColFamName(theType.getSimpleName());
      }
    }
    else if (null == tbl) {
      cfMapDef.setColFamName(cfBaseMapDef.getColFamName());
    }
    else {
      throw new IllegalStateException("Cannot specify @" + Table.class.getSimpleName() + " for derived class, "
          + cfMapDef.getClazz().getName());
    }

    processSuperclassInheritance(cfMapDef);
    processDerivedInheritance(cfMapDef);

    if (null == cfBaseMapDef) {
      cfMapByColFamName.put(cfMapDef.getColFamName(), cfMapDef);
    }
    cfMapByClazz.put(clazz, cfMapDef);

    return cfMapDef;
  }

  /**
   * Process a class marked with {@link Inheritance}.
   *
   * @param <T>
   *            Class type
   * @param <I>
   *            Object ID type
   * @param cfMapDef
   */
  private <T, I> void processSuperclassInheritance(CFMappingDef<T, I> cfMapDef) {
    Class<T> clazz = cfMapDef.getClazz();

    // if inheritance is defined then we setup for subclasses
    Inheritance inheritance = clazz.getAnnotation(Inheritance.class);
    if (null == inheritance) {
      return;
    }
    // TODO not hard-coded
    cfMapDef.setInheritanceType(InheritanceType.SINGLE_TABLE);

    DiscriminatorColumn discriminatorCol = clazz.getAnnotation(DiscriminatorColumn.class);
    if (null != discriminatorCol) {
      cfMapDef.setDiscColumn(discriminatorCol.name());
      cfMapDef.setDiscType(discriminatorCol.discriminatorType());
    }
    else {
      cfMapDef.setDiscType(DiscriminatorType.STRING);
    }

    DiscriminatorValue discriminatorValue = clazz.getAnnotation(DiscriminatorValue.class);
    if (null == discriminatorValue) {
      throw new IllegalStateException("@" + Inheritance.class.getSimpleName() + " found, but no @"
          + DiscriminatorValue.class.getSimpleName() + " - cannot continue");
    }
    cfMapDef.setDiscValue(discriminatorValue.value());
  }

  /**
   * Process a derived class with a super class marked with
   * {@link Inheritance}.
   *
   * @param <T>
   *            Super class type
   * @param <I>
   *            Object ID type
   * @param cfMapDef
   */
  private <T, I> void processDerivedInheritance(CFMappingDef<? extends T, I> cfMapDef) {
    Class<? extends T> derClazz = cfMapDef.getClazz();
    Class<T> superClazz = (Class<T>) derClazz.getSuperclass();
    if (null == superClazz) {
      return;
    }

    CFMappingDef<T, I> superCfMapDef = getCfMapDef(superClazz, false);
    if (null == superCfMapDef) {
      return;
    }

    while (null != superCfMapDef && null == superCfMapDef.getInheritanceType()) {
      superCfMapDef = (CFMappingDef<T, I>) superCfMapDef.getCfBaseMapDef();
    }

    if (null == superCfMapDef) {
      throw new IllegalStateException(derClazz.getName() + " has mapped super class, but " + superClazz.getName()
          + " isn't marked with @" + Inheritance.class.getSimpleName() + " - cannot continue");
    }

    DiscriminatorValue discriminatorValue = derClazz.getAnnotation(DiscriminatorValue.class);
    if (null == discriminatorValue) {
      throw new IllegalStateException(" found, but no @" + DiscriminatorValue.class.getSimpleName()
          + " - cannot continue");
    }
    cfMapDef.setDiscValue(discriminatorValue.value());

    cfMapDef.setCfBaseMapDef(superCfMapDef);
    superCfMapDef.addDerivedClassMap(cfMapDef);
  }
}
TOP

Related Classes of me.prettyprint.hom.ClassCacheMgr

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.