Project Hibernate 2.1.8 [5/3/05 10:18 AM]
 
Coverage - net/sf/hibernate/expression/Example.java
  //$Id: Example.java,v 1.5 2004/06/04 01:27:39 steveebersole Exp $
  package net.sf.hibernate.expression;
 
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
 
  import net.sf.hibernate.HibernateException;
  import net.sf.hibernate.engine.SessionFactoryImplementor;
  import net.sf.hibernate.engine.TypedValue;
  import net.sf.hibernate.metadata.ClassMetadata;
  import net.sf.hibernate.type.AbstractComponentType;
  import net.sf.hibernate.type.Type;
  import net.sf.hibernate.util.StringHelper;
 
  /**
   * Support for query by example.
   * <pre>
   * List results = session.createCriteria(Parent.class)
   *     .add( Example.create(parent).ignoreCase() )
   *     .createCriteria("child")
   *         .add( Example.create( parent.getChild() ) )
   *     .list();
   * </pre>
   * "Examples" may be mixed and matched with "Expressions" in the same <tt>Criteria</tt>.
   * @see net.sf.hibernate.Criteria
   * @author Gavin King
   */
  public class Example extends AbstractCriterion {
      
      private final Object entity;
22x       private final Set excludedProperties = new HashSet();
      private PropertySelector selector;
      private boolean isLikeEnabled;
      private boolean isIgnoreCaseEnabled;
      private MatchMode matchMode;
      
      /**
       * A strategy for choosing property values for inclusion in the query
       * criteria
       */
      public static interface PropertySelector {
          public boolean include(Object propertyValue, String propertyName, Type type);
      }
      
      private static final PropertySelector NOT_NULL = new NotNullPropertySelector();
      private static final PropertySelector ALL = new AllPropertySelector();
      private static final PropertySelector NOT_NULL_OR_ZERO = new NotNullOrZeroPropertySelector();
          
6x       static final class AllPropertySelector implements PropertySelector {
          public boolean include(Object object, String propertyName, Type type) { 
24x               return true;
          }
      }
      
6x       static final class NotNullPropertySelector implements PropertySelector {
          public boolean include(Object object, String propertyName, Type type) { 
2/2 128x               return object!=null;
          }
      }
      
6x       static final class NotNullOrZeroPropertySelector implements PropertySelector {
          public boolean include(Object object, String propertyName, Type type) { 
6/6 196x               return object!=null && (
                  !(object instanceof Number) || ( (Number) object ).longValue()!=0
              );
          }
      }
      
      /**
       * Set the property selector
       */
      public Example setPropertySelector(PropertySelector selector) {
10x           this.selector = selector;
10x           return this;
      }
      
      /**
       * Exclude zero-valued properties
       */
      public Example excludeZeroes() {
6x           setPropertySelector(NOT_NULL_OR_ZERO);
6x           return this;
      }
      
      /**
       * Don't exclude null or zero-valued properties
       */
      public Example excludeNone() {
4x           setPropertySelector(ALL);
4x           return this;
      }
      
      /**
       * Use the "like" operator for all string-valued properties
       */
      public Example enableLike(MatchMode matchMode) {
12x           isLikeEnabled = true;
12x           this.matchMode = matchMode;
12x           return this;
      }
      
      /**
       * Use the "like" operator for all string-valued properties
       */
      public Example enableLike() {
10x           return enableLike(MatchMode.EXACT);
      }
 
      /**
       * Ignore case for all string-valued properties
       */
      public Example ignoreCase() {
4x           isIgnoreCaseEnabled = true;
4x           return this;
      }
      
      /**
       * Exclude a particular named property
       */
      public Example excludeProperty(String name) {
16x           excludedProperties.add(name);
16x           return this;
      }
      
      /**
       * Create a new instance, which includes all non-null properties 
       * by default
       * @param entity
       * @return a new instance of <tt>Example</tt>
       */
      public static Example create(Object entity) {
1/2 22x           if (entity==null) throw new NullPointerException("null example");
22x           return new Example(entity, NOT_NULL);
      }
 
22x       protected Example(Object entity, PropertySelector selector) {
22x           this.entity = entity;
22x           this.selector = selector;
22x       }
 
      public String toString() {
0x           return entity.toString();
      }
      
      private boolean isPropertyIncluded(Object value, String name, Type type) {
6/6 504x           return !excludedProperties.contains(name) &&
              !type.isAssociationType() &&
              selector.include(value, name, type);
      }
 
      public String toSqlString(
          SessionFactoryImplementor sessionFactory,
          Class persistentClass,
          String alias,
          Map aliasClasses)
          throws HibernateException {
          
24x           StringBuffer buf = new StringBuffer().append(StringHelper.OPEN_PAREN);
24x           ClassMetadata meta = sessionFactory.getClassMetadata(persistentClass);
24x           String[] propertyNames = meta.getPropertyNames();
24x           Type[] propertyTypes = meta.getPropertyTypes();
24x           Object[] propertyValues = meta.getPropertyValues(entity);
2/2 254x           for (int i=0; i<propertyNames.length; i++) {
230x               Object propertyValue = propertyValues[i];
230x               String propertyName = propertyNames[i];
              
4/4 230x               boolean isPropertyIncluded = i!=meta.getVersionProperty() && 
                  isPropertyIncluded( propertyValue, propertyName, propertyTypes[i] );
2/2 230x               if (isPropertyIncluded) {
2/2 44x                   if ( propertyTypes[i].isComponentType() ) {
10x                       appendComponentCondition(
                          propertyName, 
                          propertyValue, 
                          (AbstractComponentType) propertyTypes[i], 
                          persistentClass,
                          alias,
                          aliasClasses,
                          sessionFactory,
                          buf
                      );
                  }
                  else {
34x                       appendPropertyCondition(
                          propertyName, 
                          propertyValue, 
                          persistentClass,
                          alias,
                          aliasClasses,
                          sessionFactory,
                          buf
                      );
                  }
              }
          }
1/2 24x           if ( buf.length()==1 ) buf.append("1=1"); //yuck!
24x           return buf.append(StringHelper.CLOSE_PAREN).toString();
      }
      
      private static final Object[] TYPED_VALUES = new TypedValue[0];
 
      public TypedValue[] getTypedValues(
          SessionFactoryImplementor sessionFactory,
          Class persistentClass,
          Map aliasClasses)
          throws HibernateException {
 
24x           ClassMetadata meta = sessionFactory.getClassMetadata(persistentClass);
24x           String[] propertyNames = meta.getPropertyNames();
24x           Type[] propertyTypes = meta.getPropertyTypes();
24x           Object[] values = meta.getPropertyValues(entity);
24x           List list = new ArrayList();
2/2 254x           for (int i=0; i<propertyNames.length; i++) {
230x               Object value = values[i];
230x               Type type = propertyTypes[i];
230x               String name = propertyNames[i];
              
4/4 230x               boolean isPropertyIncluded = i!=meta.getVersionProperty() && 
                  isPropertyIncluded(value, name, type);
              
2/2 230x               if (isPropertyIncluded) {
2/2 44x                   if ( propertyTypes[i].isComponentType() ) {
10x                       addComponentTypedValues(name, value, (AbstractComponentType) type, list);
                  }
                  else {
34x                       addPropertyTypedValue(value, type, list);
                  }
              }
          }
24x           return (TypedValue[]) list.toArray(TYPED_VALUES);
      }
      
      protected void addPropertyTypedValue(Object value, Type type, List list) {
2/2 52x           if ( value!=null ) {
2/2 50x               if ( value instanceof String ) {
30x                   String string = (String) value;
2/2 30x                   if (isIgnoreCaseEnabled) string = string.toLowerCase();
2/2 30x                   if (isLikeEnabled) string = matchMode.toMatchString(string);
30x                   value = string;
              }
50x               list.add( new TypedValue(type, value) );
          }
52x       }
      
      protected void addComponentTypedValues(String path, Object component, AbstractComponentType type, List list) 
          throws HibernateException {
          
1/2 18x           if (component!=null) {
18x               String[] propertyNames = type.getPropertyNames();
18x               Type[] subtypes = type.getSubtypes();
18x               Object[] values = type.getPropertyValues(component);
2/2 54x               for (int i=0; i<propertyNames.length; i++) {
36x                   Object value = values[i];
36x                   Type subtype = subtypes[i];
36x                   String subpath = StringHelper.qualify( path, propertyNames[i] );
2/2 36x                   if ( isPropertyIncluded(value, subpath, subtype) ) {
2/2 26x                       if ( subtype.isComponentType() ) {
8x                           addComponentTypedValues(subpath, value, (AbstractComponentType) subtype, list);
                      } 
                      else {
18x                           addPropertyTypedValue(value, subtype, list);
                      }
                  }
              }
          }
18x       }
      
      protected void appendPropertyCondition(
          String propertyName, 
          Object propertyValue, 
          Class persistentClass,
          String alias,
          Map aliasClasses,
          SessionFactoryImplementor sessionFactory,
          StringBuffer buf) 
      throws HibernateException {
          
2/2 52x           if ( buf.length()>1 ) buf.append(" and ");
          Criterion crit;
2/2 52x           if ( propertyValue!=null ) {
50x               boolean isString = propertyValue instanceof String;
8/8 50x               crit = ( isLikeEnabled && isString ) ?
                  (Criterion) new LikeExpression( propertyName, propertyValue, isIgnoreCaseEnabled ) :
                  (Criterion) new EqExpression( propertyName, propertyValue, isIgnoreCaseEnabled && isString );
                      
          }
          else {
2x               crit = new NullExpression(propertyName);
          }
52x           buf.append( crit.toSqlString(sessionFactory, persistentClass, alias, aliasClasses) );
52x       }
      
      protected void appendComponentCondition(
          String path, 
          Object component, 
          AbstractComponentType type, 
          Class persistentClass,
          String alias,
          Map aliasClasses,
          SessionFactoryImplementor sessionFactory,
          StringBuffer buf) 
      throws HibernateException {
          
1/2 18x           if (component!=null) {
18x               String[] propertyNames = type.getPropertyNames();
18x               Object[] values = type.getPropertyValues(component);
18x               Type[] subtypes = type.getSubtypes();
2/2 54x               for (int i=0; i<propertyNames.length; i++) {
36x                   String subpath = StringHelper.qualify( path, propertyNames[i] );
36x                   Object value = values[i];
2/2 36x                   if ( isPropertyIncluded( value, subpath, subtypes[i] ) ) {
26x                       Type subtype = subtypes[i];
2/2 26x                       if ( subtype.isComponentType() ) {
8x                           appendComponentCondition(
                              subpath,
                              value,
                              (AbstractComponentType) subtype,
                              persistentClass,
                              alias,
                              aliasClasses,
                              sessionFactory,
                              buf
                          );
                      } 
                      else {
18x                           appendPropertyCondition( 
                              subpath,
                              value, 
                              persistentClass,
                              alias,
                              aliasClasses,
                              sessionFactory,
                              buf
                          );
                      }
                  }
              }
          }
18x       }
  }