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
092            public String toString() {
093                StringBuffer answer = new StringBuffer();
094                answer.append(right);
095                answer.append(" ");
096                answer.append(getExpressionSymbol());
097                answer.append(" ( ");
098
099                int count = 0;
100                for (Iterator i = inList.iterator(); i.hasNext();) {
101                    Object o = (Object)i.next();
102                    if (count != 0) {
103                        answer.append(", ");
104                    }
105                    answer.append(o);
106                    count++;
107                }
108
109                answer.append(" )");
110                return answer.toString();
111            }
112
113            public String getExpressionSymbol() {
114                if (not) {
115                    return "NOT IN";
116                } else {
117                    return "IN";
118                }
119            }
120        };
121    }
122
123    abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression {
124        public BooleanUnaryExpression(Expression left) {
125            super(left);
126        }
127
128        public boolean matches(MessageEvaluationContext message) throws JMSException {
129            Object object = evaluate(message);
130            return object != null && object == Boolean.TRUE;
131        }
132    };
133
134    public static BooleanExpression createNOT(BooleanExpression left) {
135        return new BooleanUnaryExpression(left) {
136            public Object evaluate(MessageEvaluationContext message) throws JMSException {
137                Boolean lvalue = (Boolean)right.evaluate(message);
138                if (lvalue == null) {
139                    return null;
140                }
141                return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
142            }
143
144            public String getExpressionSymbol() {
145                return "NOT";
146            }
147        };
148    }
149
150    public static BooleanExpression createXPath(final String xpath) {
151        return new XPathExpression(xpath);
152    }
153
154    public static BooleanExpression createXQuery(final String xpath) {
155        return new XQueryExpression(xpath);
156    }
157
158    public static BooleanExpression createBooleanCast(Expression left) {
159        return new BooleanUnaryExpression(left) {
160            public Object evaluate(MessageEvaluationContext message) throws JMSException {
161                Object rvalue = right.evaluate(message);
162                if (rvalue == null) {
163                    return null;
164                }
165                if (!rvalue.getClass().equals(Boolean.class)) {
166                    return Boolean.FALSE;
167                }
168                return ((Boolean)rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
169            }
170
171            public String toString() {
172                return right.toString();
173            }
174
175            public String getExpressionSymbol() {
176                return "";
177            }
178        };
179    }
180
181    private static Number negate(Number left) {
182        Class clazz = left.getClass();
183        if (clazz == Integer.class) {
184            return new Integer(-left.intValue());
185        } else if (clazz == Long.class) {
186            return new Long(-left.longValue());
187        } else if (clazz == Float.class) {
188            return new Float(-left.floatValue());
189        } else if (clazz == Double.class) {
190            return new Double(-left.doubleValue());
191        } else if (clazz == BigDecimal.class) {
192            // We ussually get a big deciamal when we have Long.MIN_VALUE
193            // constant in the
194            // Selector. Long.MIN_VALUE is too big to store in a Long as a
195            // positive so we store it
196            // as a Big decimal. But it gets Negated right away.. to here we try
197            // to covert it back
198            // to a Long.
199            BigDecimal bd = (BigDecimal)left;
200            bd = bd.negate();
201
202            if (BD_LONG_MIN_VALUE.compareTo(bd) == 0) {
203                return Long.valueOf(Long.MIN_VALUE);
204            }
205            return bd;
206        } else {
207            throw new RuntimeException("Don't know how to negate: " + left);
208        }
209    }
210
211    public Expression getRight() {
212        return right;
213    }
214
215    public void setRight(Expression expression) {
216        right = expression;
217    }
218
219    /**
220     * @see java.lang.Object#toString()
221     */
222    public String toString() {
223        return "(" + getExpressionSymbol() + " " + right.toString() + ")";
224    }
225
226    /**
227     * TODO: more efficient hashCode()
228     * 
229     * @see java.lang.Object#hashCode()
230     */
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    public boolean equals(Object o) {
241
242        if (o == null || !this.getClass().equals(o.getClass())) {
243            return false;
244        }
245        return toString().equals(o.toString());
246
247    }
248
249    /**
250     * Returns the symbol that represents this binary expression. For example,
251     * addition is represented by "+"
252     * 
253     * @return
254     */
255    public abstract String getExpressionSymbol();
256
257}