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