001: /*
002: * $Id: EntityUtil.java,v 1.7 2004/02/12 20:34:12 jonesde Exp $
003: *
004: * <p>Copyright (c) 2001 The Open For Business Project - www.ofbiz.org
005: *
006: * <p>Permission is hereby granted, free of charge, to any person obtaining a
007: * copy of this software and associated documentation files (the "Software"),
008: * to deal in the Software without restriction, including without limitation
009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
010: * and/or sell copies of the Software, and to permit persons to whom the
011: * Software is furnished to do so, subject to the following conditions:
012: *
013: * <p>The above copyright notice and this permission notice shall be included
014: * in all copies or substantial portions of the Software.
015: *
016: * <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: */
024:
025: package org.ofbiz.entity.util;
026:
027: import java.sql.Timestamp;
028: import java.util.*;
029: import java.util.ArrayList;
030: import java.util.Collections;
031: import java.util.Comparator;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.LinkedList;
035: import java.util.List;
036: import java.util.Map;
037:
038: import org.ofbiz.base.util.UtilDateTime;
039: import org.ofbiz.base.util.UtilMisc;
040: import org.ofbiz.base.util.UtilValidate;
041: import org.ofbiz.entity.GenericDelegator;
042: import org.ofbiz.entity.GenericEntity;
043: import org.ofbiz.entity.GenericEntityException;
044: import org.ofbiz.entity.GenericValue;
045: import org.ofbiz.entity.condition.EntityComparisonOperator;
046: import org.ofbiz.entity.condition.EntityCondition;
047: import org.ofbiz.entity.condition.EntityConditionList;
048: import org.ofbiz.entity.condition.EntityDateFilterCondition;
049: import org.ofbiz.entity.condition.EntityExpr;
050: import org.ofbiz.entity.condition.EntityFieldMap;
051: import org.ofbiz.entity.condition.EntityOperator;
052: import org.ofbiz.entity.model.ModelField;
053:
054: /**
055: * Helper methods when dealing with Entities, especially ones that follow certain conventions
056: *
057: *@author Eric Pabst
058: *@version $ Revision: $
059: *@since 1.0
060: */
061: public class EntityUtil {
062:
063: public static GenericValue getFirst(List values) {
064: if ((values != null) && (values.size() > 0)) {
065: return (GenericValue) values.iterator().next();
066: } else {
067: return null;
068: }
069: }
070:
071: public static GenericValue getOnly(List values) {
072: if (values != null) {
073: if (values.size() <= 0) {
074: return null;
075: }
076: if (values.size() == 1) {
077: return (GenericValue) values.iterator().next();
078: } else {
079: throw new IllegalArgumentException(
080: "Passed List had more than one value.");
081: }
082: } else {
083: return null;
084: }
085: }
086:
087: public static EntityCondition getFilterByDateExpr() {
088: return new EntityDateFilterCondition("fromDate", "thruDate");
089: }
090:
091: public static EntityCondition getFilterByDateExpr(
092: String fromDateName, String thruDateName) {
093: return new EntityDateFilterCondition(fromDateName, thruDateName);
094: }
095:
096: public static EntityCondition getFilterByDateExpr(
097: java.util.Date moment) {
098: return EntityDateFilterCondition.makeCondition(
099: new java.sql.Timestamp(moment.getTime()), "fromDate",
100: "thruDate");
101: }
102:
103: public static EntityCondition getFilterByDateExpr(
104: java.sql.Timestamp moment) {
105: return EntityDateFilterCondition.makeCondition(moment,
106: "fromDate", "thruDate");
107: }
108:
109: public static EntityCondition getFilterByDateExpr(
110: java.sql.Timestamp moment, String fromDateName,
111: String thruDateName) {
112: return EntityDateFilterCondition.makeCondition(moment,
113: fromDateName, thruDateName);
114: }
115:
116: /**
117: *returns the values that are currently active.
118: *
119: *@param datedValues GenericValue's that have "fromDate" and "thruDate" fields
120: *@return List of GenericValue's that are currently active
121: */
122: public static List filterByDate(List datedValues) {
123: return filterByDate(datedValues, UtilDateTime.nowTimestamp(),
124: null, null, true);
125: }
126:
127: /**
128: *returns the values that are currently active.
129: *
130: *@param datedValues GenericValue's that have "fromDate" and "thruDate" fields
131: *@param allAreSame Specifies whether all values in the List are of the same entity; this can help speed things up a fair amount since we only have to see if the from and thru date fields are valid once
132: *@return List of GenericValue's that are currently active
133: */
134: public static List filterByDate(List datedValues, boolean allAreSame) {
135: return filterByDate(datedValues, UtilDateTime.nowTimestamp(),
136: null, null, allAreSame);
137: }
138:
139: /**
140: *returns the values that are active at the moment.
141: *
142: *@param datedValues GenericValue's that have "fromDate" and "thruDate" fields
143: *@param moment the moment in question
144: *@return List of GenericValue's that are active at the moment
145: */
146: public static List filterByDate(List datedValues,
147: java.util.Date moment) {
148: return filterByDate(datedValues, new java.sql.Timestamp(moment
149: .getTime()), null, null, true);
150: }
151:
152: /**
153: *returns the values that are active at the moment.
154: *
155: *@param datedValues GenericValue's that have "fromDate" and "thruDate" fields
156: *@param moment the moment in question
157: *@return List of GenericValue's that are active at the moment
158: */
159: public static List filterByDate(List datedValues,
160: java.sql.Timestamp moment) {
161: return filterByDate(datedValues, moment, null, null, true);
162: }
163:
164: /**
165: *returns the values that are active at the moment.
166: *
167: *@param datedValues GenericValue's that have "fromDate" and "thruDate" fields
168: *@param moment the moment in question
169: *@param allAreSame Specifies whether all values in the List are of the same entity; this can help speed things up a fair amount since we only have to see if the from and thru date fields are valid once
170: *@return List of GenericValue's that are active at the moment
171: */
172: public static List filterByDate(List datedValues,
173: java.sql.Timestamp moment, String fromDateName,
174: String thruDateName, boolean allAreSame) {
175: if (datedValues == null)
176: return null;
177: if (moment == null)
178: return datedValues;
179: if (fromDateName == null)
180: fromDateName = "fromDate";
181: if (thruDateName == null)
182: thruDateName = "thruDate";
183:
184: List result = new LinkedList();
185: Iterator iter = datedValues.iterator();
186:
187: if (allAreSame) {
188: ModelField fromDateField = null;
189: ModelField thruDateField = null;
190:
191: if (iter.hasNext()) {
192: GenericValue datedValue = (GenericValue) iter.next();
193:
194: fromDateField = datedValue.getModelEntity().getField(
195: fromDateName);
196: if (fromDateField == null)
197: throw new IllegalArgumentException("\""
198: + fromDateName + "\" is not a field of "
199: + datedValue.getEntityName());
200: thruDateField = datedValue.getModelEntity().getField(
201: thruDateName);
202: if (thruDateField == null)
203: throw new IllegalArgumentException("\""
204: + thruDateName + "\" is not a field of "
205: + datedValue.getEntityName());
206:
207: java.sql.Timestamp fromDate = (java.sql.Timestamp) datedValue
208: .dangerousGetNoCheckButFast(fromDateField);
209: java.sql.Timestamp thruDate = (java.sql.Timestamp) datedValue
210: .dangerousGetNoCheckButFast(thruDateField);
211:
212: if ((thruDate == null || thruDate.after(moment))
213: && (fromDate == null || fromDate.before(moment))) {
214: result.add(datedValue);
215: }// else not active at moment
216: }
217: while (iter.hasNext()) {
218: GenericValue datedValue = (GenericValue) iter.next();
219: java.sql.Timestamp fromDate = (java.sql.Timestamp) datedValue
220: .dangerousGetNoCheckButFast(fromDateField);
221: java.sql.Timestamp thruDate = (java.sql.Timestamp) datedValue
222: .dangerousGetNoCheckButFast(thruDateField);
223:
224: if ((thruDate == null || thruDate.after(moment))
225: && (fromDate == null || fromDate.before(moment))) {
226: result.add(datedValue);
227: }// else not active at moment
228: }
229: } else {
230: // if not all values are known to be of the same entity, must check each one...
231: while (iter.hasNext()) {
232: GenericValue datedValue = (GenericValue) iter.next();
233: java.sql.Timestamp fromDate = datedValue
234: .getTimestamp(fromDateName);
235: java.sql.Timestamp thruDate = datedValue
236: .getTimestamp(thruDateName);
237:
238: if ((thruDate == null || thruDate.after(moment))
239: && (fromDate == null || fromDate.before(moment))) {
240: result.add(datedValue);
241: }// else not active at moment
242: }
243: }
244:
245: return result;
246: }
247:
248: public static boolean isValueActive(GenericValue datedValue,
249: java.sql.Timestamp moment) {
250: return isValueActive(datedValue, moment, "fromDate", "thruDate");
251: }
252:
253: public static boolean isValueActive(GenericValue datedValue,
254: java.sql.Timestamp moment, String fromDateName,
255: String thruDateName) {
256: java.sql.Timestamp fromDate = datedValue
257: .getTimestamp(fromDateName);
258: java.sql.Timestamp thruDate = datedValue
259: .getTimestamp(thruDateName);
260:
261: if ((thruDate == null || thruDate.after(moment))
262: && (fromDate == null || fromDate.before(moment))) {
263: return true;
264: } else {
265: // else not active at moment
266: return false;
267: }
268: }
269:
270: /**
271: *returns the values that match the values in fields
272: *
273: *@param values List of GenericValues
274: *@param fields the field-name/value pairs that must match
275: *@return List of GenericValue's that match the values in fields
276: */
277: public static List filterByAnd(List values, Map fields) {
278: if (values == null)
279: return null;
280:
281: List result = null;
282:
283: if (fields == null || fields.size() == 0) {
284: result = new ArrayList(values);
285: } else {
286: result = new ArrayList(values.size());
287: Iterator iter = values.iterator();
288:
289: while (iter.hasNext()) {
290: GenericValue value = (GenericValue) iter.next();
291:
292: if (value.matchesFields(fields)) {
293: result.add(value);
294: }// else did not match
295: }
296: }
297: return result;
298: }
299:
300: /**
301: *returns the values that match all of the exprs in list
302: *
303: *@param values List of GenericValues
304: *@param exprs the expressions that must validate to true
305: *@return List of GenericValue's that match the values in fields
306: */
307: public static List filterByAnd(List values, List exprs) {
308: if (values == null)
309: return null;
310: if (exprs == null || exprs.size() == 0) {
311: // no constraints... oh well
312: return values;
313: }
314:
315: List result = new ArrayList();
316: Iterator iter = values.iterator();
317:
318: while (iter.hasNext()) {
319: GenericValue value = (GenericValue) iter.next();
320: Iterator exprIter = exprs.iterator();
321: boolean include = true;
322:
323: while (exprIter.hasNext()) {
324: EntityExpr expr = (EntityExpr) exprIter.next();
325: Object lhs = value.get((String) expr.getLhs());
326: Object rhs = expr.getRhs();
327:
328: int operatorId = expr.getOperator().getId();
329: switch (operatorId) {
330: case EntityOperator.ID_EQUALS:
331: include = EntityComparisonOperator.compareEqual(
332: lhs, rhs);
333: break;
334: case EntityOperator.ID_NOT_EQUAL:
335: include = EntityComparisonOperator.compareNotEqual(
336: lhs, rhs);
337: break;
338: case EntityOperator.ID_GREATER_THAN:
339: include = EntityComparisonOperator
340: .compareGreaterThanEqualTo(lhs, rhs);
341: break;
342: case EntityOperator.ID_GREATER_THAN_EQUAL_TO:
343: include = EntityComparisonOperator
344: .compareGreaterThan(lhs, rhs);
345: break;
346: case EntityOperator.ID_LESS_THAN:
347: include = EntityComparisonOperator.compareLessThan(
348: lhs, rhs);
349: break;
350: case EntityOperator.ID_LESS_THAN_EQUAL_TO:
351: include = EntityComparisonOperator
352: .compareLessThanEqualTo(lhs, rhs);
353: break;
354: case EntityOperator.ID_LIKE:
355: include = EntityComparisonOperator.compareLike(lhs,
356: rhs);
357: break;
358: default:
359: throw new IllegalArgumentException(
360: "The "
361: + expr.getOperator().getCode()
362: + " with id "
363: + expr.getOperator().getId()
364: + " operator is not yet supported by filterByAnd");
365: }
366: if (!include)
367: break;
368: }
369: if (include) {
370: result.add(value);
371: }
372: }
373: return result;
374: }
375:
376: /**
377: *returns the values that match any of the exprs in list
378: *
379: *@param values List of GenericValues
380: *@param exprs the expressions that must validate to true
381: *@return List of GenericValue's that match the values in fields
382: */
383: public static List filterByOr(List values, List exprs) {
384: if (values == null)
385: return null;
386: if (exprs == null || exprs.size() == 0) {
387: return values;
388: }
389:
390: List result = new ArrayList();
391: Iterator iter = values.iterator();
392:
393: while (iter.hasNext()) {
394: GenericValue value = (GenericValue) iter.next();
395: Iterator exprIter = exprs.iterator();
396: boolean include = false;
397:
398: while (exprIter.hasNext()) {
399: EntityExpr expr = (EntityExpr) exprIter.next();
400: Object lhs = value.get((String) expr.getLhs());
401: Object rhs = expr.getRhs();
402:
403: int operatorId = expr.getOperator().getId();
404: switch (operatorId) {
405: case EntityOperator.ID_EQUALS:
406: if (EntityComparisonOperator.compareEqual(lhs, rhs)) {
407: include = true;
408: }
409: break;
410: case EntityOperator.ID_NOT_EQUAL:
411: if (EntityComparisonOperator.compareNotEqual(lhs,
412: rhs)) {
413: include = true;
414: }
415: break;
416: case EntityOperator.ID_GREATER_THAN:
417: if (EntityComparisonOperator
418: .compareGreaterThanEqualTo(lhs, rhs)) {
419: include = true;
420: }
421: break;
422: case EntityOperator.ID_GREATER_THAN_EQUAL_TO:
423: if (EntityComparisonOperator.compareGreaterThan(
424: lhs, rhs)) {
425: include = true;
426: }
427: break;
428: case EntityOperator.ID_LESS_THAN:
429: if (EntityComparisonOperator.compareLessThan(lhs,
430: rhs)) {
431: include = true;
432: }
433: break;
434: case EntityOperator.ID_LESS_THAN_EQUAL_TO:
435: if (EntityComparisonOperator
436: .compareLessThanEqualTo(lhs, rhs)) {
437: include = true;
438: }
439: break;
440: case EntityOperator.ID_LIKE:
441: if (EntityComparisonOperator.compareLike(lhs, rhs)) {
442: include = true;
443: }
444: break;
445: default:
446: throw new IllegalArgumentException(
447: "The "
448: + expr.getOperator().getCode()
449: + " with id "
450: + expr.getOperator().getId()
451: + " operator is not yet supported by filterByOr");
452: }
453: if (include)
454: break;
455: }
456: if (include) {
457: result.add(value);
458: }
459: }
460: return result;
461: }
462:
463: /**
464: *returns the values in the order specified
465: *
466: *@param values List of GenericValues
467: *@param orderBy The fields of the named entity to order the query by;
468: * optionally add a " ASC" for ascending or " DESC" for descending
469: *@return List of GenericValue's in the proper order
470: */
471: public static List orderBy(Collection values, List orderBy) {
472: if (values == null)
473: return null;
474: if (values.size() == 0)
475: return new ArrayList();
476: if (orderBy == null || orderBy.size() == 0) {
477: return new ArrayList(values);
478: }
479:
480: List result = new ArrayList(values);
481: Collections.sort(result, new OrderByComparator(orderBy));
482: return result;
483: }
484:
485: public static List getRelated(String relationName, List values)
486: throws GenericEntityException {
487: if (values == null)
488: return null;
489:
490: List result = new ArrayList();
491: Iterator iter = values.iterator();
492:
493: while (iter.hasNext()) {
494: result.addAll(((GenericValue) iter.next())
495: .getRelated(relationName));
496: }
497: return result;
498: }
499:
500: public static List getRelatedCache(String relationName, List values)
501: throws GenericEntityException {
502: if (values == null)
503: return null;
504:
505: List result = new ArrayList();
506: Iterator iter = values.iterator();
507:
508: while (iter.hasNext()) {
509: result.addAll(((GenericValue) iter.next())
510: .getRelatedCache(relationName));
511: }
512: return result;
513: }
514:
515: public static List getRelatedByAnd(String relationName, Map fields,
516: List values) throws GenericEntityException {
517: if (values == null)
518: return null;
519:
520: List result = new ArrayList();
521: Iterator iter = values.iterator();
522:
523: while (iter.hasNext()) {
524: result.addAll(((GenericValue) iter.next()).getRelatedByAnd(
525: relationName, fields));
526: }
527: return result;
528: }
529:
530: public static List filterByCondition(List values,
531: EntityCondition condition) {
532: if (values == null)
533: return null;
534:
535: List result = new ArrayList(values.size());
536: Iterator iter = values.iterator();
537:
538: while (iter.hasNext()) {
539: GenericValue value = (GenericValue) iter.next();
540: if (condition.entityMatches(value)) {
541: result.add(value);
542: }
543: }
544: return result;
545: }
546:
547: public static List filterOutByCondition(List values,
548: EntityCondition condition) {
549: if (values == null)
550: return null;
551:
552: List result = new ArrayList(values.size());
553: Iterator iter = values.iterator();
554:
555: while (iter.hasNext()) {
556: GenericValue value = (GenericValue) iter.next();
557: if (!condition.entityMatches(value)) {
558: result.add(value);
559: }
560: }
561: return result;
562: }
563:
564: static class OrderByComparator implements Comparator {
565:
566: private String field;
567: private ModelField modelField = null;
568: private boolean descending;
569: private Comparator next = null;
570:
571: OrderByComparator(List orderBy) {
572: this (orderBy, 0);
573: }
574:
575: private OrderByComparator(List orderBy, int startIndex) {
576: if (orderBy == null)
577: throw new IllegalArgumentException(
578: "orderBy may not be empty");
579: if (startIndex >= orderBy.size())
580: throw new IllegalArgumentException(
581: "startIndex may not be greater than or equal to orderBy size");
582: String fieldAndDirection = (String) orderBy.get(startIndex);
583: String upper = fieldAndDirection.trim().toUpperCase();
584:
585: if (upper.endsWith(" DESC")) {
586: this .descending = true;
587: this .field = fieldAndDirection.substring(0,
588: fieldAndDirection.length() - 5);
589: } else if (upper.endsWith(" ASC")) {
590: this .descending = false;
591: this .field = fieldAndDirection.substring(0,
592: fieldAndDirection.length() - 4);
593: } else if (upper.startsWith("-")) {
594: this .descending = true;
595: this .field = fieldAndDirection.substring(1);
596: } else if (upper.startsWith("+")) {
597: this .descending = false;
598: this .field = fieldAndDirection.substring(1);
599: } else {
600: this .descending = false;
601: this .field = fieldAndDirection;
602: }
603: if (startIndex + 1 < orderBy.size()) {
604: this .next = new OrderByComparator(orderBy,
605: startIndex + 1);
606: }// else keep null
607: }
608:
609: public int compare(java.lang.Object obj, java.lang.Object obj1) {
610: int result = compareAsc((GenericEntity) obj,
611: (GenericEntity) obj1);
612:
613: if (descending && result != 0) {
614: result = -result;
615: }
616: if ((result == 0) && (next != null)) {
617: return next.compare(obj, obj1);
618: } else {
619: return result;
620: }
621: }
622:
623: private int compareAsc(GenericEntity obj, GenericEntity obj2) {
624: if (this .modelField == null) {
625: this .modelField = obj.getModelEntity().getField(field);
626: if (this .modelField == null) {
627: throw new IllegalArgumentException("The field "
628: + field
629: + " could not be found in the entity "
630: + obj.getEntityName());
631: }
632: }
633: Object value = obj
634: .dangerousGetNoCheckButFast(this .modelField);
635: Object value2 = obj2
636: .dangerousGetNoCheckButFast(this .modelField);
637:
638: // null is defined as the largest possible value
639: if (value == null)
640: return value2 == null ? 0 : 1;
641: if (value2 == null)
642: return value == null ? 0 : -1;
643: int result = ((Comparable) value).compareTo(value2);
644:
645: // if (Debug.infoOn()) Debug.logInfo("[OrderByComparator.compareAsc] Result is " + result + " for [" + value + "] and [" + value2 + "]", module);
646: return result;
647: }
648:
649: public boolean equals(java.lang.Object obj) {
650: if ((obj != null) && (obj instanceof OrderByComparator)) {
651: OrderByComparator that = (OrderByComparator) obj;
652:
653: return this .field.equals(that.field)
654: && (this .descending == that.descending)
655: && UtilValidate.areEqual(this .next, that.next);
656: } else {
657: return false;
658: }
659: }
660: }
661:
662: public static List findDatedInclusionEntity(
663: GenericDelegator delegator, String entityName, Map search)
664: throws GenericEntityException {
665: return findDatedInclusionEntity(delegator, entityName, search,
666: UtilDateTime.nowTimestamp());
667: }
668:
669: public static List findDatedInclusionEntity(
670: GenericDelegator delegator, String entityName, Map search,
671: Timestamp now) throws GenericEntityException {
672: EntityCondition searchCondition = new EntityConditionList(
673: UtilMisc.toList(new EntityFieldMap(search,
674: EntityOperator.AND), EntityUtil
675: .getFilterByDateExpr(now)), EntityOperator.AND);
676: return delegator.findByCondition(entityName, searchCondition,
677: null, UtilMisc.toList("-fromDate"));
678: }
679:
680: public static GenericValue newDatedInclusionEntity(
681: GenericDelegator delegator, String entityName, Map search)
682: throws GenericEntityException {
683: return newDatedInclusionEntity(delegator, entityName, search,
684: UtilDateTime.nowTimestamp());
685: }
686:
687: public static GenericValue newDatedInclusionEntity(
688: GenericDelegator delegator, String entityName, Map search,
689: Timestamp now) throws GenericEntityException {
690: List entities = findDatedInclusionEntity(delegator, entityName,
691: search, now);
692: if (entities != null && entities.size() > 0) {
693: search = null;
694: for (int i = 0; i < entities.size(); i++) {
695: GenericValue entity = (GenericValue) entities.get(i);
696: if (now.equals(entity.get("fromDate"))) {
697: search = new HashMap(entity.getPrimaryKey());
698: entity.remove("thruDate");
699: } else {
700: entity.set("thruDate", now);
701: }
702: entity.store();
703: }
704: if (search == null)
705: search = new HashMap(EntityUtil.getFirst(entities));
706: } else {
707: search = new HashMap(search);
708: }
709: if (now.equals(search.get("fromDate"))) {
710: return EntityUtil.getOnly(delegator.findByAnd(entityName,
711: search));
712: } else {
713: search.put("fromDate", now);
714: search.remove("thruDate");
715: return delegator.makeValue(entityName, search);
716: }
717: }
718:
719: public static void delDatedInclusionEntity(
720: GenericDelegator delegator, String entityName, Map search)
721: throws GenericEntityException {
722: delDatedInclusionEntity(delegator, entityName, search,
723: UtilDateTime.nowTimestamp());
724: }
725:
726: public static void delDatedInclusionEntity(
727: GenericDelegator delegator, String entityName, Map search,
728: Timestamp now) throws GenericEntityException {
729: List entities = findDatedInclusionEntity(delegator, entityName,
730: search, now);
731: for (int i = 0; entities != null && i < entities.size(); i++) {
732: GenericValue entity = (GenericValue) entities.get(i);
733: entity.set("thruDate", now);
734: entity.store();
735: }
736: }
737: }
|