package org.hibernate.criterion;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.hibernate.Criteria;
import org.hibernate.EntityMode;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;
public class Example implements Criterion {
private final Object exampleEntity;
private PropertySelector selector;
private boolean isLikeEnabled;
private Character escapeCharacter;
private boolean isIgnoreCaseEnabled;
private MatchMode matchMode;
private final Set<String> excludedProperties = new HashSet<String>();
public static Example create(Object exampleEntity) {
if ( exampleEntity == null ) {
throw new NullPointerException( "null example entity" );
}
return new Example( exampleEntity, NotNullPropertySelector.INSTANCE );
}
protected Example(Object exampleEntity, PropertySelector selector) {
this.exampleEntity = exampleEntity;
this.selector = selector;
}
public Example setEscapeCharacter(Character escapeCharacter) {
this.escapeCharacter = escapeCharacter;
return this;
}
public Example enableLike() {
return enableLike( MatchMode.EXACT );
}
public Example enableLike(MatchMode matchMode) {
this.isLikeEnabled = true;
this.matchMode = matchMode;
return this;
}
public Example ignoreCase() {
this.isIgnoreCaseEnabled = true;
return this;
}
public Example setPropertySelector(PropertySelector selector) {
this.selector = selector;
return this;
}
public Example excludeZeroes() {
setPropertySelector( NotNullOrZeroPropertySelector.INSTANCE );
return this;
}
public Example excludeNone() {
setPropertySelector( AllPropertySelector.INSTANCE );
return this;
}
public Example excludeProperty(String name) {
excludedProperties.add( name );
return this;
}
@Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) {
final StringBuilder buf = new StringBuilder().append( '(' );
final EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
criteriaQuery.getEntityName( criteria )
);
final String[] propertyNames = meta.getPropertyNames();
final Type[] propertyTypes = meta.getPropertyTypes();
final Object[] propertyValues = meta.getPropertyValues( exampleEntity );
for ( int i=0; i<propertyNames.length; i++ ) {
final Object propertyValue = propertyValues[i];
final String propertyName = propertyNames[i];
final boolean isVersionProperty = i == meta.getVersionProperty();
if ( ! isVersionProperty && isPropertyIncluded( propertyValue, propertyName, propertyTypes[i] ) ) {
if ( propertyTypes[i].isComponentType() ) {
appendComponentCondition(
propertyName,
propertyValue,
(CompositeType) propertyTypes[i],
criteria,
criteriaQuery,
buf
);
}
else {
appendPropertyCondition(
propertyName,
propertyValue,
criteria,
criteriaQuery,
buf
);
}
}
}
if ( buf.length()==1 ) {
buf.append( "1=1" );
}
return buf.append( ')' ).toString();
}
@SuppressWarnings("SimplifiableIfStatement")
private boolean isPropertyIncluded(Object value, String name, Type type) {
if ( excludedProperties.contains( name ) ) {
return false;
}
if ( type.isAssociationType() ) {
return false;
}
return selector.include( value, name, type );
}
@Override
public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) {
final EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
criteriaQuery.getEntityName( criteria )
);
final String[] propertyNames = meta.getPropertyNames();
final Type[] propertyTypes = meta.getPropertyTypes();
final Object[] values = meta.getPropertyValues( exampleEntity );
final List<TypedValue> list = new ArrayList<TypedValue>();
for ( int i=0; i<propertyNames.length; i++ ) {
final Object value = values[i];
final Type type = propertyTypes[i];
final String name = propertyNames[i];
final boolean isVersionProperty = i == meta.getVersionProperty();
if ( ! isVersionProperty && isPropertyIncluded( value, name, type ) ) {
if ( propertyTypes[i].isComponentType() ) {
addComponentTypedValues( name, value, (CompositeType) type, list, criteria, criteriaQuery );
}
else {
addPropertyTypedValue( value, type, list );
}
}
}
return list.toArray( new TypedValue[ list.size() ] );
}
protected void addPropertyTypedValue(Object value, Type type, List<TypedValue> list) {
if ( value != null ) {
if ( value instanceof String ) {
String string = (String) value;
if ( isIgnoreCaseEnabled ) {
string = string.toLowerCase(Locale.ROOT);
}
if ( isLikeEnabled ) {
string = matchMode.toMatchString( string );
}
value = string;
}
list.add( new TypedValue( type, value ) );
}
}
protected void addComponentTypedValues(
String path,
Object component,
CompositeType type,
List<TypedValue> list,
Criteria criteria,
CriteriaQuery criteriaQuery) {
if ( component != null ) {
final String[] propertyNames = type.getPropertyNames();
final Type[] subtypes = type.getSubtypes();
final Object[] values = type.getPropertyValues( component, getEntityMode( criteria, criteriaQuery ) );
for ( int i=0; i<propertyNames.length; i++ ) {
final Object value = values[i];
final Type subtype = subtypes[i];
final String subpath = StringHelper.qualify( path, propertyNames[i] );
if ( isPropertyIncluded( value, subpath, subtype ) ) {
if ( subtype.isComponentType() ) {
addComponentTypedValues( subpath, value, (CompositeType) subtype, list, criteria, criteriaQuery );
}
else {
addPropertyTypedValue( value, subtype, list );
}
}
}
}
}
private EntityMode getEntityMode(Criteria criteria, CriteriaQuery criteriaQuery) {
final EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
criteriaQuery.getEntityName( criteria )
);
final EntityMode result = meta.getEntityMode();
if ( ! meta.getEntityMetamodel().getTuplizer().isInstance( exampleEntity ) ) {
throw new ClassCastException( exampleEntity.getClass().getName() );
}
return result;
}
protected void appendPropertyCondition(
String propertyName,
Object propertyValue,
Criteria criteria,
CriteriaQuery cq,
StringBuilder buf) {
final Criterion condition;
if ( propertyValue != null ) {
final boolean isString = propertyValue instanceof String;
if ( isLikeEnabled && isString ) {
condition = new LikeExpression(
propertyName,
(String) propertyValue,
matchMode,
escapeCharacter,
isIgnoreCaseEnabled
);
}
else {
condition = new SimpleExpression( propertyName, propertyValue, "=", isIgnoreCaseEnabled && isString );
}
}
else {
condition = new NullExpression(propertyName);
}
final String conditionFragment = condition.toSqlString( criteria, cq );
if ( conditionFragment.trim().length() > 0 ) {
if ( buf.length() > 1 ) {
buf.append( " and " );
}
buf.append( conditionFragment );
}
}
protected void appendComponentCondition(
String path,
Object component,
CompositeType type,
Criteria criteria,
CriteriaQuery criteriaQuery,
StringBuilder buf) {
if ( component != null ) {
final String[] propertyNames = type.getPropertyNames();
final Object[] values = type.getPropertyValues( component, getEntityMode( criteria, criteriaQuery ) );
final Type[] subtypes = type.getSubtypes();
for ( int i=0; i<propertyNames.length; i++ ) {
final String subPath = StringHelper.qualify( path, propertyNames[i] );
final Object value = values[i];
if ( isPropertyIncluded( value, subPath, subtypes[i] ) ) {
final Type subtype = subtypes[i];
if ( subtype.isComponentType() ) {
appendComponentCondition(
subPath,
value,
(CompositeType) subtype,
criteria,
criteriaQuery,
buf
);
}
else {
appendPropertyCondition(
subPath,
value,
criteria,
criteriaQuery,
buf
);
}
}
}
}
}
@Override
public String toString() {
return "example (" + exampleEntity + ')';
}
public static interface PropertySelector extends Serializable {
public boolean include(Object propertyValue, String propertyName, Type type);
}
public static final class AllPropertySelector implements PropertySelector {
public static final AllPropertySelector INSTANCE = new AllPropertySelector();
@Override
public boolean include(Object object, String propertyName, Type type) {
return true;
}
private Object readResolve() {
return INSTANCE;
}
}
public static final class NotNullPropertySelector implements PropertySelector {
public static final NotNullPropertySelector INSTANCE = new NotNullPropertySelector();
@Override
public boolean include(Object object, String propertyName, Type type) {
return object!=null;
}
private Object readResolve() {
return INSTANCE;
}
}
public static final class NotNullOrZeroPropertySelector implements PropertySelector {
public static final NotNullOrZeroPropertySelector INSTANCE = new NotNullOrZeroPropertySelector();
@Override
public boolean include(Object object, String propertyName, Type type) {
return object != null
&& ( !(object instanceof Number) || ( (Number) object ).longValue()!=0
);
}
private Object readResolve() {
return INSTANCE;
}
}
}