/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. *******************************************************************************/ package org.apache.ofbiz.minilang.method.conditional; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import org.apache.ofbiz.base.util.Assert; import org.apache.ofbiz.base.util.ObjectType; import org.apache.ofbiz.base.util.UtilGenerics; import org.apache.ofbiz.entity.GenericEntity; import org.apache.ofbiz.minilang.MiniLangUtil; /** * Implements a comparison - consisting of an l-value, an r-value, and a comparison operator. */ public abstract class Compare { private static final Map<String, Compare> INSTANCE_MAP = createInstanceMap(); private static void assertValuesNotNull(Object lValue, Object rValue) { if (lValue == null) { throw new IllegalArgumentException("Cannot compare: l-value is null"); } if (rValue == null) { throw new IllegalArgumentException("Cannot compare: r-value is null"); } } private static int compareBigDecimals(BigDecimal lBigDecimal, BigDecimal rBigDecimal) { // Developers: Do not change this. We are comparing two fixed-point decimal numbers, // not performing accounting calculations - so it is okay to specify the rounding mode. int decimals = lBigDecimal.scale(); if (rBigDecimal.scale() < decimals) { lBigDecimal = lBigDecimal.setScale(rBigDecimal.scale(), RoundingMode.UP); } else if (decimals < rBigDecimal.scale()) { rBigDecimal = rBigDecimal.setScale(decimals, RoundingMode.UP); } return lBigDecimal.compareTo(rBigDecimal); } private static Map<String, Compare> createInstanceMap() { Map<String, Compare> writableMap = new HashMap<String, Compare>(10); writableMap.put("contains", new CompareContains()); writableMap.put("equals", new CompareEquals()); writableMap.put("greater", new CompareGreater()); writableMap.put("greater-equals", new CompareGreaterEquals()); writableMap.put("is-empty", new CompareIsEmpty()); writableMap.put("is-not-null", new CompareIsNotNull()); writableMap.put("is-null", new CompareIsNull()); writableMap.put("less", new CompareLess()); writableMap.put("less-equals", new CompareLessEquals()); writableMap.put("not-equals", new CompareNotEquals()); return Collections.unmodifiableMap(writableMap); } /** * Returns a <code>Compare</code> instance for the specified operator. * * @param operator * @return A <code>Compare</code> instance for the specified operator */ public static Compare getInstance(String operator) { Assert.notNull("operator", operator); return INSTANCE_MAP.get(operator); } /** * Returns the result of this comparison. * * @param lValue The object being compared * @param rValue The object being compared to * @param type The Java class to be used in the comparison * @param format Optional format to be used in object type conversions * @param locale Optional locale to be used in object type conversions * @param timeZone Optional time zone to be used in object type conversions * @return The result of this comparison * @throws Exception */ public abstract boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception; private static final class CompareContains extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { // The type parameter is ignored when using the contains operator, so no conversions will be performed. if (lValue == null || lValue == GenericEntity.NULL_FIELD) { return false; } if (lValue instanceof Collection) { Collection<Object> collection = UtilGenerics.checkCollection(lValue); return collection.contains(rValue); } if (lValue instanceof String && rValue instanceof String) { return ((String) lValue).contains((String) rValue); } throw new IllegalArgumentException("Cannot compare: l-value is not a collection"); } } private static final class CompareEquals extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); Object convertedRvalue = MiniLangUtil.convertType(rValue, type, locale, timeZone, format); if (convertedLvalue == null) { return convertedRvalue == null; } if (convertedRvalue == null) { return false; } if (convertedLvalue instanceof BigDecimal && convertedRvalue instanceof BigDecimal) { BigDecimal lBigDecimal = (BigDecimal) convertedLvalue; BigDecimal rBigDecimal = (BigDecimal) convertedRvalue; return compareBigDecimals(lBigDecimal, rBigDecimal) == 0; } if (convertedLvalue instanceof Comparable && convertedRvalue instanceof Comparable) { Comparable<Object> comparable = UtilGenerics.cast(convertedLvalue); return comparable.compareTo(convertedRvalue) == 0; } return convertedLvalue.equals(convertedRvalue); } } private static final class CompareGreater extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); Object convertedRvalue = MiniLangUtil.convertType(rValue, type, locale, timeZone, format); assertValuesNotNull(convertedLvalue, convertedRvalue); if (convertedLvalue instanceof BigDecimal && convertedRvalue instanceof BigDecimal) { BigDecimal lBigDecimal = (BigDecimal) convertedLvalue; BigDecimal rBigDecimal = (BigDecimal) convertedRvalue; return compareBigDecimals(lBigDecimal, rBigDecimal) > 0; } if (convertedLvalue instanceof Comparable && convertedRvalue instanceof Comparable) { Comparable<Object> comparable = UtilGenerics.cast(convertedLvalue); return comparable.compareTo(convertedRvalue) > 0; } throw new IllegalArgumentException("Cannot compare: l-value is not a comparable type"); } } private static final class CompareGreaterEquals extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); Object convertedRvalue = MiniLangUtil.convertType(rValue, type, locale, timeZone, format); assertValuesNotNull(convertedLvalue, convertedRvalue); if (convertedLvalue instanceof BigDecimal && convertedRvalue instanceof BigDecimal) { BigDecimal lBigDecimal = (BigDecimal) convertedLvalue; BigDecimal rBigDecimal = (BigDecimal) convertedRvalue; return compareBigDecimals(lBigDecimal, rBigDecimal) >= 0; } if (convertedLvalue instanceof Comparable && convertedRvalue instanceof Comparable) { Comparable<Object> comparable = UtilGenerics.cast(convertedLvalue); return comparable.compareTo(convertedRvalue) >= 0; } throw new IllegalArgumentException("Cannot compare: l-value is not a comparable type"); } } private static final class CompareIsEmpty extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); return ObjectType.isEmpty(convertedLvalue); } } private static final class CompareIsNotNull extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); return convertedLvalue != null; } } private static final class CompareIsNull extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); return convertedLvalue == null; } } private static final class CompareLess extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); Object convertedRvalue = MiniLangUtil.convertType(rValue, type, locale, timeZone, format); assertValuesNotNull(convertedLvalue, convertedRvalue); if (convertedLvalue instanceof BigDecimal && convertedRvalue instanceof BigDecimal) { BigDecimal lBigDecimal = (BigDecimal) convertedLvalue; BigDecimal rBigDecimal = (BigDecimal) convertedRvalue; return compareBigDecimals(lBigDecimal, rBigDecimal) < 0; } if (convertedLvalue instanceof Comparable && convertedRvalue instanceof Comparable) { Comparable<Object> comparable = UtilGenerics.cast(convertedLvalue); return comparable.compareTo(convertedRvalue) < 0; } throw new IllegalArgumentException("Cannot compare: l-value is not a comparable type"); } } private static final class CompareLessEquals extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); Object convertedRvalue = MiniLangUtil.convertType(rValue, type, locale, timeZone, format); assertValuesNotNull(convertedLvalue, convertedRvalue); if (convertedLvalue instanceof BigDecimal && convertedRvalue instanceof BigDecimal) { BigDecimal lBigDecimal = (BigDecimal) convertedLvalue; BigDecimal rBigDecimal = (BigDecimal) convertedRvalue; return compareBigDecimals(lBigDecimal, rBigDecimal) <= 0; } if (convertedLvalue instanceof Comparable && convertedRvalue instanceof Comparable) { Comparable<Object> comparable = UtilGenerics.cast(convertedLvalue); return comparable.compareTo(convertedRvalue) <= 0; } throw new IllegalArgumentException("Cannot compare: l-value is not a comparable type"); } } private static final class CompareNotEquals extends Compare { @Override public boolean doCompare(Object lValue, Object rValue, Class<?> type, Locale locale, TimeZone timeZone, String format) throws Exception { Object convertedLvalue = MiniLangUtil.convertType(lValue, type, locale, timeZone, format); Object convertedRvalue = MiniLangUtil.convertType(rValue, type, locale, timeZone, format); if (convertedLvalue == null) { return convertedRvalue != null; } if (convertedRvalue == null) { return true; } if (convertedLvalue instanceof BigDecimal && convertedRvalue instanceof BigDecimal) { BigDecimal lBigDecimal = (BigDecimal) convertedLvalue; BigDecimal rBigDecimal = (BigDecimal) convertedRvalue; return compareBigDecimals(lBigDecimal, rBigDecimal) != 0; } if (convertedLvalue instanceof Comparable && convertedRvalue instanceof Comparable) { Comparable<Object> comparable = UtilGenerics.cast(convertedLvalue); return comparable.compareTo(convertedRvalue) != 0; } return !convertedLvalue.equals(convertedRvalue); } } }