001: /*******************************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *******************************************************************************/package org.ofbiz.entity.finder;
019:
020: import java.io.Serializable;
021: import java.util.Collection;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.LinkedList;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.Set;
029:
030: import org.ofbiz.base.util.Debug;
031: import org.ofbiz.base.util.UtilValidate;
032: import org.ofbiz.base.util.StringUtil;
033: import org.ofbiz.base.util.ObjectType;
034: import org.ofbiz.base.util.UtilFormatOut;
035: import org.ofbiz.base.util.UtilXml;
036: import org.ofbiz.base.util.collections.FlexibleMapAccessor;
037: import org.ofbiz.base.util.string.FlexibleStringExpander;
038: import org.ofbiz.entity.GenericDelegator;
039: import org.ofbiz.entity.GenericEntityException;
040: import org.ofbiz.entity.condition.EntityComparisonOperator;
041: import org.ofbiz.entity.condition.EntityCondition;
042: import org.ofbiz.entity.condition.EntityConditionList;
043: import org.ofbiz.entity.condition.EntityExpr;
044: import org.ofbiz.entity.condition.EntityJoinOperator;
045: import org.ofbiz.entity.condition.EntityOperator;
046: import org.ofbiz.entity.model.ModelEntity;
047: import org.ofbiz.entity.util.EntityListIterator;
048: import org.w3c.dom.Element;
049:
050: /**
051: * Uses the delegator to find entity values by a condition
052: *
053: */
054: public class EntityFinderUtil {
055:
056: public static final String module = EntityFinderUtil.class
057: .getName();
058:
059: public static Map makeFieldMap(Element element) {
060: Map fieldMap = null;
061: List fieldMapElementList = UtilXml.childElementList(element,
062: "field-map");
063: if (fieldMapElementList.size() > 0) {
064: fieldMap = new HashMap();
065: Iterator fieldMapElementIter = fieldMapElementList
066: .iterator();
067: while (fieldMapElementIter.hasNext()) {
068: Element fieldMapElement = (Element) fieldMapElementIter
069: .next();
070: // set the env-name for each field-name, noting that if no field-name is specified it defaults to the env-name
071: String fieldName = fieldMapElement
072: .getAttribute("field-name");
073: String envName = fieldMapElement
074: .getAttribute("env-name");
075: String value = fieldMapElement.getAttribute("value");
076: if (UtilValidate.isEmpty(fieldName)) {
077: // no fieldName, use envName for both
078: fieldMap.put(new FlexibleMapAccessor(envName),
079: new FlexibleMapAccessor(envName));
080: } else {
081: if (UtilValidate.isNotEmpty(value)) {
082: fieldMap.put(
083: new FlexibleMapAccessor(fieldName),
084: new FlexibleStringExpander(value));
085: } else {
086: // at this point we have a fieldName and no value, do we have a envName?
087: if (UtilValidate.isNotEmpty(envName)) {
088: fieldMap.put(new FlexibleMapAccessor(
089: fieldName),
090: new FlexibleMapAccessor(envName));
091: } else {
092: // no envName, use fieldName for both
093: fieldMap.put(new FlexibleMapAccessor(
094: fieldName),
095: new FlexibleMapAccessor(fieldName));
096: }
097: }
098: }
099: }
100: }
101: return fieldMap;
102: }
103:
104: public static void expandFieldMapToContext(Map fieldMap,
105: Map context, Map outContext) {
106: //Debug.logInfo("fieldMap: " + fieldMap, module);
107: if (fieldMap != null) {
108: Iterator fieldMapEntryIter = fieldMap.entrySet().iterator();
109: while (fieldMapEntryIter.hasNext()) {
110: Map.Entry entry = (Map.Entry) fieldMapEntryIter.next();
111: FlexibleMapAccessor serviceContextFieldAcsr = (FlexibleMapAccessor) entry
112: .getKey();
113: Object valueSrc = entry.getValue();
114: if (valueSrc instanceof FlexibleMapAccessor) {
115: FlexibleMapAccessor contextEnvAcsr = (FlexibleMapAccessor) valueSrc;
116: serviceContextFieldAcsr.put(outContext,
117: contextEnvAcsr.get(context));
118: } else if (valueSrc instanceof FlexibleStringExpander) {
119: FlexibleStringExpander valueExdr = (FlexibleStringExpander) valueSrc;
120: serviceContextFieldAcsr.put(outContext, valueExdr
121: .expandString(context));
122: } else {
123: // hmmmm...
124: }
125: }
126: }
127: }
128:
129: public static List makeSelectFieldExpanderList(Element element) {
130: List selectFieldExpanderList = null;
131: List selectFieldElementList = UtilXml.childElementList(element,
132: "select-field");
133: if (selectFieldElementList.size() > 0) {
134: selectFieldExpanderList = new LinkedList();
135: Iterator selectFieldElementIter = selectFieldElementList
136: .iterator();
137: while (selectFieldElementIter.hasNext()) {
138: Element selectFieldElement = (Element) selectFieldElementIter
139: .next();
140: selectFieldExpanderList.add(new FlexibleStringExpander(
141: selectFieldElement.getAttribute("field-name")));
142: }
143: }
144: return selectFieldExpanderList;
145: }
146:
147: public static Set makeFieldsToSelect(List selectFieldExpanderList,
148: Map context) {
149: Set fieldsToSelect = null;
150: if (selectFieldExpanderList != null
151: && selectFieldExpanderList.size() > 0) {
152: fieldsToSelect = new HashSet();
153: Iterator selectFieldExpanderIter = selectFieldExpanderList
154: .iterator();
155: while (selectFieldExpanderIter.hasNext()) {
156: FlexibleStringExpander selectFieldExpander = (FlexibleStringExpander) selectFieldExpanderIter
157: .next();
158: fieldsToSelect.add(selectFieldExpander
159: .expandString(context));
160: }
161: }
162: return fieldsToSelect;
163: }
164:
165: public static List makeOrderByFieldList(List orderByExpanderList,
166: Map context) {
167: List orderByFields = null;
168: if (orderByExpanderList != null
169: && orderByExpanderList.size() > 0) {
170: orderByFields = new LinkedList();
171: Iterator orderByExpanderIter = orderByExpanderList
172: .iterator();
173: while (orderByExpanderIter.hasNext()) {
174: FlexibleStringExpander orderByExpander = (FlexibleStringExpander) orderByExpanderIter
175: .next();
176: orderByFields
177: .add(orderByExpander.expandString(context));
178: }
179: }
180: return orderByFields;
181: }
182:
183: public static interface Condition extends Serializable {
184: public EntityCondition createCondition(Map context,
185: String entityName, GenericDelegator delegator);
186: }
187:
188: public static class ConditionExpr implements Condition {
189: protected FlexibleStringExpander fieldNameExdr;
190: protected FlexibleStringExpander operatorExdr;
191: protected FlexibleMapAccessor envNameAcsr;
192: protected FlexibleStringExpander valueExdr;
193: protected boolean ignoreIfNull;
194: protected boolean ignoreIfEmpty;
195: protected boolean ignoreCase;
196:
197: public ConditionExpr(Element conditionExprElement) {
198: this .fieldNameExdr = new FlexibleStringExpander(
199: conditionExprElement.getAttribute("field-name"));
200: if (this .fieldNameExdr.isEmpty()) {
201: // no "field-name"? try "name"
202: this .fieldNameExdr = new FlexibleStringExpander(
203: conditionExprElement.getAttribute("name"));
204: }
205:
206: this .operatorExdr = new FlexibleStringExpander(
207: UtilFormatOut.checkEmpty(conditionExprElement
208: .getAttribute("operator"), "equals"));
209: this .envNameAcsr = new FlexibleMapAccessor(
210: conditionExprElement.getAttribute("env-name"));
211: this .valueExdr = new FlexibleStringExpander(
212: conditionExprElement.getAttribute("value"));
213: this .ignoreIfNull = "true".equals(conditionExprElement
214: .getAttribute("ignore-if-null"));
215: this .ignoreIfEmpty = "true".equals(conditionExprElement
216: .getAttribute("ignore-if-empty"));
217: this .ignoreCase = "true".equals(conditionExprElement
218: .getAttribute("ignore-case"));
219: }
220:
221: public EntityCondition createCondition(Map context,
222: String entityName, GenericDelegator delegator) {
223: ModelEntity modelEntity = delegator
224: .getModelEntity(entityName);
225: if (modelEntity == null) {
226: throw new IllegalArgumentException(
227: "Error in Entity Find: could not find entity with name ["
228: + entityName + "]");
229: }
230:
231: String fieldName = fieldNameExdr.expandString(context);
232:
233: Object value = null;
234: // start with the environment variable, will override if exists and a value is specified
235: if (envNameAcsr != null) {
236: value = envNameAcsr.get(context);
237: }
238: // no value so far, and a string value is specified, use that
239: if (value == null && valueExdr != null) {
240: value = valueExdr.expandString(context);
241: }
242:
243: String operatorName = operatorExdr.expandString(context);
244: EntityOperator operator = EntityOperator
245: .lookup(operatorName);
246: if (operator == null) {
247: throw new IllegalArgumentException(
248: "Could not find an entity operator for the name: "
249: + operatorName);
250: }
251:
252: // If IN operator, see if value is a literal list and split it
253: if (operator == EntityOperator.IN
254: && value instanceof String) {
255: String delim = null;
256: if (((String) value).indexOf("|") >= 0) {
257: delim = "|";
258: } else if (((String) value).indexOf(",") >= 0) {
259: delim = ",";
260: }
261: if (UtilValidate.isNotEmpty(delim)) {
262: value = StringUtil.split((String) value, delim);
263: }
264: }
265:
266: // don't convert the field to the desired type if this is an IN operator and we have a Collection
267: if (!(operator == EntityOperator.IN && value instanceof Collection)) {
268: // now to a type conversion for the target fieldName
269: value = modelEntity.convertFieldValue(fieldName, value,
270: delegator);
271: }
272:
273: if (Debug.verboseOn())
274: Debug.logVerbose("Got value for fieldName ["
275: + fieldName + "]: " + value, module);
276:
277: if (this .ignoreIfNull && value == null) {
278: return null;
279: }
280: if (this .ignoreIfEmpty && ObjectType.isEmpty(value)) {
281: return null;
282: }
283:
284: if (operator == EntityOperator.NOT_EQUAL && value != null) {
285: // since some databases don't consider nulls in != comparisons, explicitly include them
286: // this makes more sense logically, but if anyone ever needs it to not behave this way we should add an "or-null" attribute that is true by default
287: if (ignoreCase) {
288: return new EntityExpr(new EntityExpr(fieldName,
289: true, (EntityComparisonOperator) operator,
290: value, true), EntityOperator.OR,
291: new EntityExpr(fieldName,
292: EntityOperator.EQUALS, null));
293: } else {
294: return new EntityExpr(
295: new EntityExpr(
296: fieldName,
297: (EntityComparisonOperator) operator,
298: value), EntityOperator.OR,
299: new EntityExpr(fieldName,
300: EntityOperator.EQUALS, null));
301: }
302: } else {
303: if (ignoreCase) {
304: // use the stuff to upper case both sides
305: return new EntityExpr(fieldName, true,
306: (EntityComparisonOperator) operator, value,
307: true);
308: } else {
309: return new EntityExpr(fieldName,
310: (EntityComparisonOperator) operator, value);
311: }
312: }
313: }
314: }
315:
316: public static class ConditionList implements Condition {
317: List conditionList = new LinkedList();
318: FlexibleStringExpander combineExdr;
319:
320: public ConditionList(Element conditionListElement) {
321: this .combineExdr = new FlexibleStringExpander(
322: conditionListElement.getAttribute("combine"));
323:
324: List subElements = UtilXml
325: .childElementList(conditionListElement);
326: Iterator subElementIter = subElements.iterator();
327: while (subElementIter.hasNext()) {
328: Element subElement = (Element) subElementIter.next();
329: if ("condition-expr".equals(subElement.getNodeName())) {
330: conditionList.add(new ConditionExpr(subElement));
331: } else if ("condition-list".equals(subElement
332: .getNodeName())) {
333: conditionList.add(new ConditionList(subElement));
334: } else if ("condition-object".equals(subElement
335: .getNodeName())) {
336: conditionList.add(new ConditionObject(subElement));
337: } else {
338: throw new IllegalArgumentException(
339: "Invalid element with name ["
340: + subElement.getNodeName()
341: + "] found under a condition-list element.");
342: }
343: }
344: }
345:
346: public EntityCondition createCondition(Map context,
347: String entityName, GenericDelegator delegator) {
348: if (this .conditionList.size() == 0) {
349: return null;
350: }
351: if (this .conditionList.size() == 1) {
352: Condition condition = (Condition) this .conditionList
353: .get(0);
354: return condition.createCondition(context, entityName,
355: delegator);
356: }
357:
358: List entityConditionList = new LinkedList();
359: Iterator conditionIter = conditionList.iterator();
360: while (conditionIter.hasNext()) {
361: Condition curCondition = (Condition) conditionIter
362: .next();
363: EntityCondition econd = curCondition.createCondition(
364: context, entityName, delegator);
365: if (econd != null) {
366: entityConditionList.add(econd);
367: }
368: }
369:
370: String operatorName = combineExdr.expandString(context);
371: EntityOperator operator = EntityOperator
372: .lookup(operatorName);
373: if (operator == null) {
374: throw new IllegalArgumentException(
375: "Could not find an entity operator for the name: "
376: + operatorName);
377: }
378:
379: return new EntityConditionList(entityConditionList,
380: (EntityJoinOperator) operator);
381: }
382: }
383:
384: public static class ConditionObject implements Condition {
385: protected FlexibleMapAccessor fieldNameAcsr;
386:
387: public ConditionObject(Element conditionExprElement) {
388: this .fieldNameAcsr = new FlexibleMapAccessor(
389: conditionExprElement.getAttribute("field-name"));
390: if (this .fieldNameAcsr.isEmpty()) {
391: // no "field-name"? try "name"
392: this .fieldNameAcsr = new FlexibleMapAccessor(
393: conditionExprElement.getAttribute("name"));
394: }
395: }
396:
397: public EntityCondition createCondition(Map context,
398: String entityName, GenericDelegator delegator) {
399: EntityCondition condition = (EntityCondition) fieldNameAcsr
400: .get(context);
401: return condition;
402: }
403: }
404:
405: public static interface OutputHandler extends Serializable {
406: public void handleOutput(EntityListIterator eli, Map context,
407: FlexibleMapAccessor listAcsr);
408:
409: public void handleOutput(List results, Map context,
410: FlexibleMapAccessor listAcsr);
411: }
412:
413: public static class LimitRange implements OutputHandler {
414: FlexibleStringExpander startExdr;
415: FlexibleStringExpander sizeExdr;
416:
417: public LimitRange(Element limitRangeElement) {
418: this .startExdr = new FlexibleStringExpander(
419: limitRangeElement.getAttribute("start"));
420: this .sizeExdr = new FlexibleStringExpander(
421: limitRangeElement.getAttribute("size"));
422: }
423:
424: int getStart(Map context) {
425: String startStr = this .startExdr.expandString(context);
426: try {
427: return Integer.parseInt(startStr);
428: } catch (NumberFormatException e) {
429: String errMsg = "The limit-range start number \""
430: + startStr + "\" was not valid: "
431: + e.toString();
432: Debug.logError(e, errMsg, module);
433: throw new IllegalArgumentException(errMsg);
434: }
435: }
436:
437: int getSize(Map context) {
438: String sizeStr = this .sizeExdr.expandString(context);
439: try {
440: return Integer.parseInt(sizeStr);
441: } catch (NumberFormatException e) {
442: String errMsg = "The limit-range size number \""
443: + sizeStr + "\" was not valid: " + e.toString();
444: Debug.logError(e, errMsg, module);
445: throw new IllegalArgumentException(errMsg);
446: }
447: }
448:
449: public void handleOutput(EntityListIterator eli, Map context,
450: FlexibleMapAccessor listAcsr) {
451: int start = getStart(context);
452: int size = getSize(context);
453: try {
454: listAcsr.put(context, eli.getPartialList(start, size));
455: eli.close();
456: } catch (GenericEntityException e) {
457: String errMsg = "Error getting partial list in limit-range with start="
458: + start
459: + " and size="
460: + size
461: + ": "
462: + e.toString();
463: Debug.logError(e, errMsg, module);
464: throw new IllegalArgumentException(errMsg);
465: }
466: }
467:
468: public void handleOutput(List results, Map context,
469: FlexibleMapAccessor listAcsr) {
470: int start = getStart(context);
471: int size = getSize(context);
472:
473: int end = start + size;
474: if (end > results.size())
475: end = results.size();
476:
477: listAcsr.put(context, results.subList(start, end));
478: }
479: }
480:
481: public static class LimitView implements OutputHandler {
482: FlexibleStringExpander viewIndexExdr;
483: FlexibleStringExpander viewSizeExdr;
484:
485: public LimitView(Element limitViewElement) {
486: this .viewIndexExdr = new FlexibleStringExpander(
487: limitViewElement.getAttribute("view-index"));
488: this .viewSizeExdr = new FlexibleStringExpander(
489: limitViewElement.getAttribute("view-size"));
490: }
491:
492: int getIndex(Map context) {
493: String viewIndexStr = this .viewIndexExdr
494: .expandString(context);
495: try {
496: return Integer.parseInt(viewIndexStr);
497: } catch (NumberFormatException e) {
498: String errMsg = "The limit-view view-index number \""
499: + viewIndexStr + "\" was not valid: "
500: + e.toString();
501: Debug.logError(e, errMsg, module);
502: throw new IllegalArgumentException(errMsg);
503: }
504: }
505:
506: int getSize(Map context) {
507: String viewSizeStr = this .viewSizeExdr
508: .expandString(context);
509: try {
510: return Integer.parseInt(viewSizeStr);
511: } catch (NumberFormatException e) {
512: String errMsg = "The limit-view view-size number \""
513: + viewSizeStr + "\" was not valid: "
514: + e.toString();
515: Debug.logError(e, errMsg, module);
516: throw new IllegalArgumentException(errMsg);
517: }
518: }
519:
520: public void handleOutput(EntityListIterator eli, Map context,
521: FlexibleMapAccessor listAcsr) {
522: int index = this .getIndex(context);
523: int size = this .getSize(context);
524:
525: try {
526: listAcsr.put(context, eli.getPartialList(
527: ((index - 1) * size) + 1, size));
528: eli.close();
529: } catch (GenericEntityException e) {
530: String errMsg = "Error getting partial list in limit-view with index="
531: + index
532: + " and size="
533: + size
534: + ": "
535: + e.toString();
536: Debug.logError(e, errMsg, module);
537: throw new IllegalArgumentException(errMsg);
538: }
539: }
540:
541: public void handleOutput(List results, Map context,
542: FlexibleMapAccessor listAcsr) {
543: int index = this .getIndex(context);
544: int size = this .getSize(context);
545:
546: int begin = index * size;
547: int end = index * size + size;
548: if (end > results.size())
549: end = results.size();
550:
551: listAcsr.put(context, results.subList(begin, end));
552: }
553: }
554:
555: public static class UseIterator implements OutputHandler {
556: public UseIterator(Element useIteratorElement) {
557: // no parameters, nothing to do
558: }
559:
560: public void handleOutput(EntityListIterator eli, Map context,
561: FlexibleMapAccessor listAcsr) {
562: listAcsr.put(context, eli);
563: }
564:
565: public void handleOutput(List results, Map context,
566: FlexibleMapAccessor listAcsr) {
567: throw new IllegalArgumentException(
568: "Cannot handle output with use-iterator when the query is cached, or the result in general is not an EntityListIterator");
569: }
570: }
571:
572: public static class GetAll implements OutputHandler {
573: public GetAll() {
574: // no parameters, nothing to do
575: }
576:
577: public void handleOutput(EntityListIterator eli, Map context,
578: FlexibleMapAccessor listAcsr) {
579: try {
580: listAcsr.put(context, eli.getCompleteList());
581: eli.close();
582: } catch (GenericEntityException e) {
583: String errorMsg = "Error getting list from EntityListIterator: "
584: + e.toString();
585: Debug.logError(e, errorMsg, module);
586: throw new IllegalArgumentException(errorMsg);
587: }
588: }
589:
590: public void handleOutput(List results, Map context,
591: FlexibleMapAccessor listAcsr) {
592: listAcsr.put(context, results);
593: }
594: }
595: }
|