text: package play.modules.search.store;
  
  import java.util.Collection;
  
  import javax.persistence.Id;
  import javax.persistence.ManyToOne;
  
  import org.apache.lucene.document.Document;
  import org.apache.lucene.document.Field;
  
  import play.Logger;
  import play.data.binding.Binder;
  import play.db.jpa.Blob;
  import play.db.jpa.JPABase;
  import play.db.jpa.Model;
  import play.exceptions.UnexpectedException;
  import play.modules.search.Indexed;
  
  /**
   * Various utils handling object to index and query result to object conversion
   * 
   * @author jfp
   */
  public class ConvertionUtils {
      /**
       * Examines a JPABase object and creates the corresponding Lucene Document
       * 
       * @param object to examine, expected a JPABase object
       * @return the corresponding Lucene document
       * @throws Exception
       */
      public static Document toDocument(Object object) throws Exception {
          Indexed indexed = object.getClass().getAnnotation(Indexed.class);
          if (indexed == null)
              return null;
          if (!(object instanceof JPABase))
              return null;
          JPABase jpaBase = (JPABase) object;
          Document document = new Document();
          document.add(new Field("_docID", getIdValueFor(jpaBase) + "", Field.Store.YES, Field.Index.NOT_ANALYZED));
          StringBuffer allValue = new StringBuffer();
          for (java.lang.reflect.Field field : object.getClass().getFields()) {
              play.modules.search.Field index = field.getAnnotation(play.modules.search.Field.class);
              if (index == null)
                  continue;
              if (field.getType().isArray())
                  continue;
              if (Collection.class.isAssignableFrom(field.getType()))
                  continue;
  
              String name = field.getName();
              String value = null;
  
              if (JPABase.class.isAssignableFrom(field.getType()) && !(index.joinField().length() == 0)) {
                  JPABase joinObject = (JPABase ) field.get(object);
                  for (java.lang.reflect.Field joinField : joinObject.getClass().getFields()) {
                      if (joinField.getName().equals(index.joinField())) {
                          name = joinField.getName();
                          value = valueOf(joinObject, joinField);
                      }
                  }
              } else {
                  value = valueOf(object, field);
              }
  
              if (value == null)
                  continue;
  
              document.add(new Field(name, value, index.stored() ? Field.Store.YES : Field.Store.NO,
                              index.tokenize() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED));
              if (index.tokenize() && index.sortable()) {
                  document.add(new Field(name + "_untokenized", value, index.stored() ? Field.Store.YES : Field.Store.NO,
                                  Field.Index.NOT_ANALYZED));
              }
              allValue.append(value).append(' ');
          }
          document.add(new Field("allfield", allValue.toString(), Field.Store.NO, Field.Index.ANALYZED));
          return document;
      }
  
      public static String valueOf(Object object, java.lang.reflect.Field field) throws Exception {
          if (field.getType().equals(String.class)) {
              return (String ) field.get(object);
          }
          if (field.getType().equals(Blob.class) && field.get(object) != null) {
              return FileExtractor.getText((Blob) field.get(object));
          }
  
          Object o = field.get(object);
          if (field.isAnnotationPresent(ManyToOne.class) && o instanceof JPABase) {
              return "" + getIdValueFor((JPABase ) o);
          }
  
          return "" + field.get(object);
      }
  
      /**
       * Looks for the type of the id fiels on the JPABase target class and use
       * play's binder to retrieve the corresponding object used to build JPA load
       * query
       * 
       * @param clazz JPABase target class
       * @param indexValue String value of the id, taken from index
       * @return Object id expected to build query
       */
      public static Object getIdValueFromIndex(Class<?> clazz, String indexValue) {
          java.lang.reflect.Field field = getIdField(clazz);
          Class<?> parameter = field.getType();
          try {
              return Binder.directBind(indexValue, parameter);
          } catch (Exception e) {
              throw new UnexpectedException("Could not convert the ID from index to corresponding type", e);
          }
      }
  
      /**
       * Find a ID field on the JPABase target class
       * 
       * @param clazz JPABase target class
       * @return corresponding field
       */
      public static java.lang.reflect.Field getIdField(Class<?> clazz) {
          for (java.lang.reflect.Field field : clazz.getFields()) {
              if (field.getAnnotation(Id.class) != null) {
                  return field;
              }
          }
          throw new RuntimeException("Your class " + clazz.getName()
                          + " is annotated with javax.persistence.Id but the field Id was not found");
      }
  
      /**
       * Lookups the id field, being a Long id for Model and an annotated field @Id
       * for JPABase and returns the field value.
       * 
       * @param jpaBase is a Play! Framework that supports JPA
       * @return the field value (a Long or a String for UUID)
       */
      public static Object getIdValueFor(JPABase jpaBase) {
          if (jpaBase instanceof Model) {
              return ((Model ) jpaBase).id;
          }
  
          java.lang.reflect.Field field = getIdField(jpaBase.getClass());
          Object val = null;
          try {
              val = field.get(jpaBase);
          } catch (IllegalAccessException e) {
              Logger.error("Unable to read the field value of a field annotated with @Id " + field.getName() + " due to "
                              + e.getMessage(), e);
          }
          return val;
      }
  
      public static boolean isForcedUntokenized(Class<?> clazz, String fieldName) {
          try {
              java.lang.reflect.Field field = clazz.getField(fieldName);
              play.modules.search.Field index = field.getAnnotation(play.modules.search.Field.class);
              return index.tokenize() && index.sortable();
          } catch (Exception e) {
              Logger.error("%s", e.getCause());
          }
          return false;
      }
  }
  