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     */
017    package org.apache.activemq.filter;
018    
019    import java.math.BigDecimal;
020    import java.util.Collection;
021    import java.util.HashSet;
022    import java.util.Iterator;
023    import java.util.List;
024    
025    import javax.jms.JMSException;
026    
027    /**
028     * An expression which performs an operation on two expression values
029     * 
030     * 
031     */
032    public 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    }