001    /**
002     * =========================================
003     * LibFormula : a free Java formula library
004     * =========================================
005     *
006     * Project Info:  http://reporting.pentaho.org/libformula/
007     *
008     * (C) Copyright 2006-2007, by Pentaho Corporation and Contributors.
009     *
010     * This library is free software; you can redistribute it and/or modify it under the terms
011     * of the GNU Lesser General Public License as published by the Free Software Foundation;
012     * either version 2.1 of the License, or (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016     * See the GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public License along with this
019     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020     * Boston, MA 02111-1307, USA.
021     *
022     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023     * in the United States and other countries.]
024     *
025     *
026     * ------------
027     * $Id: DefaultComparator.java 3521 2007-10-16 10:55:14Z tmorgner $
028     * ------------
029     * (C) Copyright 2006-2007, by Pentaho Corporation.
030     */
031    package org.jfree.formula.typing;
032    
033    import java.math.BigDecimal;
034    
035    import org.jfree.formula.FormulaContext;
036    import org.jfree.util.ObjectUtilities;
037    
038    /**
039     * Creation-Date: 03.11.2006, 16:15:28
040     * 
041     * @author Thomas Morgner
042     */
043    public class DefaultComparator implements ExtendedComparator
044    {
045      private FormulaContext context;
046    
047      public static final Integer LESS = new Integer(-1);
048    
049      public static final Integer EQUAL = new Integer(0);
050    
051      private static final Integer MORE = new Integer(1);
052    
053      public DefaultComparator()
054      {
055      }
056    
057      public void inititalize(final FormulaContext context)
058      {
059        this.context = context;
060      }
061    
062      public boolean isEqual(final Type type1, final Object value1,
063          final Type type2, final Object value2)
064      {
065        // this is rather easy. If at least one of the types is a numeric,
066        // try to compare them as numbers. (And here it gets messy.)
067    
068        final TypeRegistry typeRegistry = context.getTypeRegistry();
069        try
070        {
071          final Number number1 = typeRegistry.convertToNumber(type1, value1);
072          final Number number2 = typeRegistry.convertToNumber(type2, value2);
073          final BigDecimal bd1 = new BigDecimal(number1.toString());
074          final BigDecimal bd2 = new BigDecimal(number2.toString());
075          if (bd1.signum() != bd2.signum())
076          {
077            return false;
078          }
079    
080          final BigDecimal result = bd1.subtract(bd2);
081          return (result.signum() == 0);
082        }
083        catch (TypeConversionException nfe)
084        {
085          // ignore ..
086        }
087    
088        if (type1.isFlagSet(Type.TEXT_TYPE) || type2.isFlagSet(Type.TEXT_TYPE))
089        {
090          String text1 = null;
091          String text2 = null;
092          try
093          {
094            // Convert both values to text ..
095            text1 = typeRegistry.convertToText(type1, value1);
096            text2 = typeRegistry.convertToText(type2, value2);
097          }
098          catch (TypeConversionException nfe)
099          {
100            // ignore ..
101          }
102    
103          if (text1 == null && text2 == null)
104          {
105            return true;
106          }
107          if (text1 == null || text2 == null)
108          {
109            return false;
110          }
111          return ObjectUtilities.equal(text1, text2);
112    
113        }
114    
115        // Fall back to Java's equals method and hope the best ..
116        return (ObjectUtilities.equal(value1, value2));
117      }
118    
119      /**
120       * Returns null, if the types are not comparable and are not convertible at
121       * all.
122       * 
123       * @param type1
124       * @param value1
125       * @param type2
126       * @param value2
127       * @return
128       */
129      public Integer compare(final Type type1, final Object value1,
130          final Type type2, final Object value2)
131      {
132        // this is rather easy. If at least one of the types is a numeric,
133        // try to compare them as numbers. (And here it gets messy.)
134        if (value1 == null && value2 == null)
135        {
136          return DefaultComparator.EQUAL;
137        }
138        if (value1 == null)
139        {
140          return DefaultComparator.LESS;
141        }
142        if (value2 == null)
143        {
144          return DefaultComparator.MORE;
145        }
146    
147        // First, we try to compare both types directly. This is the least-expensive
148        // solution, as it does
149        // not include any conversion operations ..
150        if (type1.isFlagSet(Type.SCALAR_TYPE) && type2.isFlagSet(Type.SCALAR_TYPE))
151        {
152          // this is something else
153          if (value1 instanceof Comparable && value2 instanceof Comparable)
154          {
155            final Comparable c1 = (Comparable) value1;
156            try
157            {
158              final int result = c1.compareTo(value2);
159              if (result == 0)
160              {
161                return DefaultComparator.EQUAL;
162              }
163              else if (result > 0)
164              {
165                return DefaultComparator.MORE;
166              }
167              else
168              {
169                return DefaultComparator.LESS;
170              }
171            }
172            catch (Exception e)
173            {
174              // ignore any exception ..
175            }
176          }
177        }
178    
179        // Next, we check the types on a numeric level.
180        final TypeRegistry typeRegistry = context.getTypeRegistry();
181        try
182        {
183          final Number number1 = typeRegistry.convertToNumber(type1, value1);
184          final Number number2 = typeRegistry.convertToNumber(type2, value2);
185          final BigDecimal bd1 = new BigDecimal(number1.toString());
186          final BigDecimal bd2 = new BigDecimal(number2.toString());
187    
188          if (bd1.signum() != bd2.signum())
189          {
190            if (bd1.signum() < 0)
191            {
192              return DefaultComparator.LESS;
193            }
194            else if (bd1.signum() > 0)
195            {
196              return DefaultComparator.MORE;
197            }
198          }
199    
200          final BigDecimal result = bd1.subtract(bd2);
201          if (result.signum() == 0)
202          {
203            return DefaultComparator.EQUAL;
204          }
205          if (result.signum() > 0)
206          {
207            return DefaultComparator.MORE;
208          }
209          return DefaultComparator.LESS;
210        }
211        catch (TypeConversionException nfe)
212        {
213          // Ignore ..
214        }
215    
216        // And finally convert them to text and compare the text values ..
217        // Convert both values to text ..
218        String text1 = null;
219        String text2 = null;
220        try
221        {
222          text1 = typeRegistry.convertToText(type1, value1);
223          text2 = typeRegistry.convertToText(type2, value2);
224        }
225        catch (TypeConversionException e)
226        {
227          // failure here can be ignored.
228        }
229    
230        if (text1 == null && text2 == null)
231        {
232          return DefaultComparator.EQUAL;
233        }
234        if (text1 == null)
235        {
236          return DefaultComparator.LESS;
237        }
238        if (text2 == null)
239        {
240          return DefaultComparator.MORE;
241        }
242    
243        final int result = text1.compareTo(text2);
244        if (result == 0)
245        {
246          return DefaultComparator.EQUAL;
247        }
248        else if (result > 0)
249        {
250          return DefaultComparator.MORE;
251        }
252        else
253        {
254          return DefaultComparator.LESS;
255        }
256      }
257    }