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