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     */
022    package org.granite.gravity.selector;
023    
024    import java.math.BigDecimal;
025    import java.util.Collection;
026    import java.util.HashSet;
027    import java.util.Iterator;
028    import java.util.List;
029    
030    import javax.jms.JMSException;
031    
032    /**
033     * An expression which performs an operation on two expression values
034     *
035     * @version $Revision: 1.3 $
036     */
037    public 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    }