DataCheckbox component
[aramzamzam-commons.git] / aramzamzam-commons / commons-hibernate / src / main / java / net / aramzamzam / commons / hibernate / utils / AssociationExample.java
blobe2d751577211f9a8e2d6b0d9a351c8fc70f694a0
1 package net.aramzamzam.commons.hibernate.utils;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Set;
9 import org.hibernate.Criteria;
10 import org.hibernate.EntityMode;
11 import org.hibernate.HibernateException;
12 import org.hibernate.criterion.CriteriaQuery;
13 import org.hibernate.criterion.Criterion;
14 import org.hibernate.criterion.MatchMode;
15 import org.hibernate.criterion.Restrictions;
16 import org.hibernate.criterion.SimpleExpression;
17 import org.hibernate.engine.TypedValue;
18 import org.hibernate.persister.entity.EntityPersister;
19 import org.hibernate.type.AbstractComponentType;
20 import org.hibernate.type.Type;
21 import org.hibernate.util.StringHelper;
23 /**
24 * A copy of Hibernate's Example class, with modifications that allow you to
25 * include many-to-one and one-to-one associations in Query By Example (QBE)
26 * queries.
28 * @author original code by Gavin King, modified by jkelly
29 * @see <a href="http://forum.hibernate.org/viewtopic.php?t=942872">Example and
30 * association class </a>
32 @SuppressWarnings("unchecked")
33 public class AssociationExample implements Criterion {
34 private static final long serialVersionUID = 4909881342527136958L;
36 private final Object entity;
37 private final Set excludedProperties = new HashSet();
38 private PropertySelector selector;
39 private boolean isLikeEnabled;
40 private boolean isIgnoreCaseEnabled;
41 private MatchMode matchMode;
42 private boolean includeAssociations = true;
44 /**
45 * A strategy for choosing property values for inclusion in the query
46 * criteria
49 public static interface PropertySelector {
50 public boolean include(Object propertyValue, String propertyName,
51 Type type);
54 private static final PropertySelector NOT_NULL = new NotNullPropertySelector();
55 private static final PropertySelector ALL = new AllPropertySelector();
56 private static final PropertySelector NOT_NULL_OR_ZERO = new NotNullOrZeroPropertySelector();
58 static final class AllPropertySelector implements PropertySelector {
59 public boolean include(Object object, String propertyName, Type type) {
60 return true;
64 static final class NotNullPropertySelector implements PropertySelector {
65 public boolean include(Object object, String propertyName, Type type) {
66 return object != null;
70 static final class NotNullOrZeroPropertySelector implements
71 PropertySelector {
72 public boolean include(Object object, String propertyName, Type type) {
73 return object != null
74 && (!(object instanceof Number) || ((Number) object)
75 .longValue() != 0);
79 /**
80 * Set the property selector
82 public AssociationExample setPropertySelector(PropertySelector selector) {
83 this.selector = selector;
84 return this;
87 /**
88 * Exclude zero-valued properties
90 public AssociationExample excludeZeroes() {
91 setPropertySelector(NOT_NULL_OR_ZERO);
92 return this;
95 /**
96 * Don't exclude null or zero-valued properties
98 public AssociationExample excludeNone() {
99 setPropertySelector(ALL);
100 return this;
104 * Use the "like" operator for all string-valued properties
106 public AssociationExample enableLike(MatchMode matchMode) {
107 isLikeEnabled = true;
108 this.matchMode = matchMode;
109 return this;
113 * Use the "like" operator for all string-valued properties
115 public AssociationExample enableLike() {
116 return enableLike(MatchMode.EXACT);
120 * Ignore case for all string-valued properties
122 public AssociationExample ignoreCase() {
123 isIgnoreCaseEnabled = true;
124 return this;
128 * Exclude a particular named property
130 public AssociationExample excludeProperty(String name) {
131 excludedProperties.add(name);
132 return this;
136 * Create a new instance, which includes all non-null properties by default
138 * @param entity
139 * @return a new instance of <tt>Example</tt>
141 public static AssociationExample create(Object entity) {
142 if (entity == null)
143 throw new NullPointerException("null AssociationExample");
144 return new AssociationExample(entity, NOT_NULL);
147 protected AssociationExample(Object entity, PropertySelector selector) {
148 this.entity = entity;
149 this.selector = selector;
152 public String toString() {
153 return "example (" + entity + ')';
156 private boolean isPropertyIncluded(Object value, String name, Type type) {
157 return !excludedProperties.contains(name)
158 && selector.include(value, name, type)
159 && (!type.isAssociationType() || (type.isAssociationType()
160 && includeAssociations && !type.isCollectionType()));
163 public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
164 throws HibernateException {
166 StringBuffer buf = new StringBuffer().append('(');
167 EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
168 criteriaQuery.getEntityName(criteria));
169 List propertyNames = new ArrayList();
170 propertyNames.addAll(Arrays.asList(meta.getPropertyNames()));
172 List propertyTypes = new ArrayList();
173 propertyTypes.addAll(Arrays.asList(meta.getPropertyTypes()));
175 // TODO: get all properties, not just the fetched ones!
176 List propertyValues = new ArrayList();
177 propertyValues.addAll(Arrays.asList(meta.getPropertyValues(entity,
178 getEntityMode(criteria, criteriaQuery))));
179 if (meta.getIdentifierPropertyName() != null) {
180 propertyNames.add(meta.getIdentifierPropertyName());
181 propertyTypes.add(meta.getIdentifierType());
182 propertyValues.add(meta.getIdentifier(entity, getEntityMode(
183 criteria, criteriaQuery)));
186 for (int i = 0; i < propertyNames.size(); i++) {
187 Object propertyValue = propertyValues.get(i);
188 String propertyName = (String) propertyNames.get(i);
190 boolean isPropertyIncluded = i != meta.getVersionProperty()
191 && isPropertyIncluded(propertyValue, propertyName,
192 (Type) propertyTypes.get(i));
193 if (isPropertyIncluded) {
194 if (((Type) propertyTypes.get(i)).isComponentType()) {
195 appendComponentCondition(propertyName, propertyValue,
196 (AbstractComponentType) propertyTypes.get(i),
197 criteria, criteriaQuery, buf);
198 } else {
199 appendPropertyCondition(propertyName, propertyValue,
200 criteria, criteriaQuery, buf);
204 if (buf.length() == 1)
205 buf.append("1=1"); // yuck!
206 return buf.append(')').toString();
209 private static final Object[] TYPED_VALUES = new TypedValue[0];
211 public TypedValue[] getTypedValues(Criteria criteria,
212 CriteriaQuery criteriaQuery) throws HibernateException {
214 EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
215 criteriaQuery.getEntityName(criteria));
217 List propertyNames = new ArrayList();
218 propertyNames.addAll(Arrays.asList(meta.getPropertyNames()));
220 List propertyTypes = new ArrayList();
221 propertyTypes.addAll(Arrays.asList(meta.getPropertyTypes()));
223 // TODO: get all properties, not just the fetched ones!
224 List values = new ArrayList();
225 values.addAll(Arrays.asList(meta.getPropertyValues(entity,
226 getEntityMode(criteria, criteriaQuery))));
227 if (meta.getIdentifierPropertyName() != null) {
228 propertyNames.add(meta.getIdentifierPropertyName());
229 propertyTypes.add(meta.getIdentifierType());
230 values.add(meta.getIdentifier(entity, getEntityMode(criteria,
231 criteriaQuery)));
233 List list = new ArrayList();
234 for (int i = 0; i < propertyNames.size(); i++) {
235 Object value = values.get(i);
236 Type type = (Type) propertyTypes.get(i);
237 String name = (String) propertyNames.get(i);
239 boolean isPropertyIncluded = i != meta.getVersionProperty()
240 && isPropertyIncluded(value, name, type);
242 if (isPropertyIncluded) {
243 if (((Type) propertyTypes.get(i)).isComponentType()) {
244 addComponentTypedValues(name, value,
245 (AbstractComponentType) type, list, criteria,
246 criteriaQuery);
247 } else {
248 addPropertyTypedValue(value, type, list);
252 return (TypedValue[]) list.toArray(TYPED_VALUES);
255 private EntityMode getEntityMode(Criteria criteria,
256 CriteriaQuery criteriaQuery) {
257 EntityPersister meta = criteriaQuery.getFactory().getEntityPersister(
258 criteriaQuery.getEntityName(criteria));
259 EntityMode result = meta.guessEntityMode(entity);
260 if (result == null) {
261 throw new ClassCastException(entity.getClass().getName());
263 return result;
266 protected void addPropertyTypedValue(Object value, Type type, List list) {
267 if (value != null) {
268 if (value instanceof String) {
269 String string = (String) value;
270 if (isIgnoreCaseEnabled)
271 string = string.toLowerCase();
272 if (isLikeEnabled)
273 string = matchMode.toMatchString(string);
274 value = string;
276 list.add(new TypedValue(type, value, null));
280 protected void addComponentTypedValues(String path, Object component,
281 AbstractComponentType type, List list, Criteria criteria,
282 CriteriaQuery criteriaQuery) throws HibernateException {
284 if (component != null) {
285 String[] propertyNames = type.getPropertyNames();
286 Type[] subtypes = type.getSubtypes();
287 Object[] values = type.getPropertyValues(component, getEntityMode(
288 criteria, criteriaQuery));
289 for (int i = 0; i < propertyNames.length; i++) {
290 Object value = values[i];
291 Type subtype = subtypes[i];
292 String subpath = StringHelper.qualify(path, propertyNames[i]);
293 if (isPropertyIncluded(value, subpath, subtype)) {
294 if (subtype.isComponentType()) {
295 addComponentTypedValues(subpath, value,
296 (AbstractComponentType) subtype, list,
297 criteria, criteriaQuery);
298 } else {
299 addPropertyTypedValue(value, subtype, list);
306 protected void appendPropertyCondition(String propertyName,
307 Object propertyValue, Criteria criteria, CriteriaQuery cq,
308 StringBuffer buf) throws HibernateException {
309 Criterion crit;
310 if (propertyValue != null) {
311 boolean isString = propertyValue instanceof String;
312 SimpleExpression se = (isLikeEnabled && isString) ? Restrictions
313 .like(propertyName, propertyValue) : Restrictions.eq(
314 propertyName, propertyValue);
315 crit = (isIgnoreCaseEnabled && isString) ? se.ignoreCase() : se;
316 } else {
317 crit = Restrictions.isNull(propertyName);
319 String critCondition = crit.toSqlString(criteria, cq);
320 if (buf.length() > 1 && critCondition.trim().length() > 0)
321 buf.append(" and ");
322 buf.append(critCondition);
325 protected void appendComponentCondition(String path, Object component,
326 AbstractComponentType type, Criteria criteria,
327 CriteriaQuery criteriaQuery, StringBuffer buf)
328 throws HibernateException {
330 if (component != null) {
331 String[] propertyNames = type.getPropertyNames();
332 Object[] values = type.getPropertyValues(component, getEntityMode(
333 criteria, criteriaQuery));
334 Type[] subtypes = type.getSubtypes();
335 for (int i = 0; i < propertyNames.length; i++) {
336 String subpath = StringHelper.qualify(path, propertyNames[i]);
337 Object value = values[i];
338 if (isPropertyIncluded(value, subpath, subtypes[i])) {
339 Type subtype = subtypes[i];
340 if (subtype.isComponentType()) {
341 appendComponentCondition(subpath, value,
342 (AbstractComponentType) subtype, criteria,
343 criteriaQuery, buf);
344 } else {
345 appendPropertyCondition(subpath, value, criteria,
346 criteriaQuery, buf);
353 public boolean isIncludeAssociations() {
354 return includeAssociations;
357 public void setIncludeAssociations(boolean includeAssociations) {
358 this.includeAssociations = includeAssociations;