001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.gravity.selector;
023
024import java.math.BigDecimal;
025import java.util.Collection;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.List;
029
030import javax.jms.JMSException;
031
032/**
033 * An expression which performs an operation on two expression values
034 *
035 * @version $Revision: 1.3 $
036 */
037public abstract class UnaryExpression implements Expression {
038
039    private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE);
040    protected Expression right;
041
042    public static Expression createNegate(Expression left) {
043        return new UnaryExpression(left) {
044            public Object evaluate(MessageEvaluationContext message) throws JMSException {
045                Object rvalue = right.evaluate(message);
046                if (rvalue == null) {
047                    return null;
048                }
049                if (rvalue instanceof Number) {
050                    return negate((Number) rvalue);
051                }
052                return null;
053            }
054
055            @Override
056            public String getExpressionSymbol() {
057                return "-";
058            }
059        };
060    }
061
062    public static BooleanExpression createInExpression(PropertyExpression right, List<?> elements, final boolean not) {
063
064        // Use a HashSet if there are many elements.
065        Collection<?> t;
066        if( elements.size()==0 )
067            t=null;
068        else if( elements.size() < 5 )
069            t = elements;
070        else {
071            t = new HashSet<Object>(elements);
072        }
073        final Collection<?> inList = t;
074
075        return new BooleanUnaryExpression(right) {
076            public Object evaluate(MessageEvaluationContext message) throws JMSException {
077
078                Object rvalue = right.evaluate(message);
079                if (rvalue == null) {
080                    return null;
081                }
082                if( rvalue.getClass()!=String.class )
083                    return null;
084
085                if( (inList!=null && inList.contains(rvalue)) ^ not ) {
086                    return Boolean.TRUE;
087                }
088                return Boolean.FALSE;
089
090            }
091
092            @Override
093            public String toString() {
094                StringBuffer answer = new StringBuffer();
095                answer.append(right);
096                answer.append(" ");
097                answer.append(getExpressionSymbol());
098                answer.append(" ( ");
099
100                if (inList != null) {
101                        int count=0;
102                        for (Iterator<?> i = inList.iterator(); i.hasNext();) {
103                            Object o = i.next();
104                            if( count!=0 ) {
105                                answer.append(", ");
106                            }
107                            answer.append(o);
108                            count++;
109                        }
110                }
111
112                answer.append(" )");
113                return answer.toString();
114            }
115
116            @Override
117            public String getExpressionSymbol() {
118                if( not )
119                    return "NOT IN";
120                return "IN";
121            }
122        };
123    }
124
125    abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression {
126        public BooleanUnaryExpression(Expression left) {
127            super(left);
128        }
129
130        public boolean matches(MessageEvaluationContext message) throws JMSException {
131            Object object = evaluate(message);
132            return object!=null && object == Boolean.TRUE;
133        }
134    }
135
136
137    public static BooleanExpression createNOT(BooleanExpression left) {
138        return new BooleanUnaryExpression(left) {
139            public Object evaluate(MessageEvaluationContext message) throws JMSException {
140                Boolean lvalue = (Boolean) right.evaluate(message);
141                if (lvalue == null) {
142                    return null;
143                }
144                return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
145            }
146
147            @Override
148            public String getExpressionSymbol() {
149                return "NOT";
150            }
151        };
152    }
153
154    public static BooleanExpression createBooleanCast(Expression left) {
155        return new BooleanUnaryExpression(left) {
156            public Object evaluate(MessageEvaluationContext message) throws JMSException {
157                Object rvalue = right.evaluate(message);
158                if (rvalue == null)
159                    return null;
160                if (!rvalue.getClass().equals(Boolean.class))
161                    return Boolean.FALSE;
162                return ((Boolean)rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
163            }
164
165            @Override
166            public String toString() {
167                return right.toString();
168            }
169
170            @Override
171            public String getExpressionSymbol() {
172                return "";
173            }
174        };
175    }
176
177    private static Number negate(Number left) {
178        Class<?> clazz = left.getClass();
179        if (clazz == Integer.class) {
180            return new Integer(-left.intValue());
181        }
182        else if (clazz == Long.class) {
183            return new Long(-left.longValue());
184        }
185        else if (clazz ==  Float.class) {
186            return new Float(-left.floatValue());
187        }
188        else if (clazz == Double.class) {
189            return new Double(-left.doubleValue());
190        }
191        else if (clazz == BigDecimal.class) {
192            // We ussually get a big deciamal when we have Long.MIN_VALUE constant in the
193            // Selector.  Long.MIN_VALUE is too big to store in a Long as a positive so we store it
194            // as a Big decimal.  But it gets Negated right away.. to here we try to covert it back
195            // to a Long.
196            BigDecimal bd = (BigDecimal)left;
197            bd = bd.negate();
198
199            if( BD_LONG_MIN_VALUE.compareTo(bd)==0  ) {
200                return new Long(Long.MIN_VALUE);
201            }
202            return bd;
203        }
204        else {
205            throw new RuntimeException("Don't know how to negate: "+left);
206        }
207    }
208
209    public UnaryExpression(Expression left) {
210        this.right = left;
211    }
212
213    public Expression getRight() {
214        return right;
215    }
216
217    public void setRight(Expression expression) {
218        right = expression;
219    }
220
221    /**
222     * @see java.lang.Object#toString()
223     */
224    @Override
225    public String toString() {
226        return "(" + getExpressionSymbol() + " " + right.toString() + ")";
227    }
228
229    /**
230     * TODO: more efficient hashCode()
231     *
232     * @see java.lang.Object#hashCode()
233     */
234    @Override
235    public int hashCode() {
236        return toString().hashCode();
237    }
238
239    /**
240     * TODO: more efficient hashCode()
241     *
242     * @see java.lang.Object#equals(java.lang.Object)
243     */
244    @Override
245    public boolean equals(Object o) {
246
247        if (o == null || !this.getClass().equals(o.getClass())) {
248            return false;
249        }
250        return toString().equals(o.toString());
251
252    }
253
254    /**
255     * Returns the symbol that represents this binary expression.  For example, addition is
256     * represented by "+"
257     *
258     * @return teh symbol
259     */
260    abstract public String getExpressionSymbol();
261
262}