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 javax.jms.JMSException;
020import java.util.ArrayList;
021import java.util.List;
022
023/**
024 * A sequence of expressions, to be combined with OR or AND conjunctions.
025 *
026 */
027public abstract class LogicExpression implements BooleanExpression {
028
029    protected final List<BooleanExpression> expressions = new ArrayList<>(2);
030
031    private LogicExpression(BooleanExpression lvalue, BooleanExpression rvalue) {
032        expressions.add(lvalue);
033        expressions.add(rvalue);
034    }
035
036    protected void addExpression(BooleanExpression expression) {
037        expressions.add(expression);
038    }
039
040    public BooleanExpression getLeft() {
041        if (expressions.size() == 2) {
042            return expressions.get(0);
043        }
044        throw new IllegalStateException("This expression is not binary: " + this);
045    }
046
047    public BooleanExpression getRight() {
048        if (expressions.size() == 2) {
049            return expressions.get(1);
050        }
051        throw new IllegalStateException("This expression is not binary: " + this);
052    }
053
054    /**
055     * Returns the symbol that represents this binary expression.  For example, addition is
056     * represented by "+"
057     *
058     * @return
059     */
060    public abstract String getExpressionSymbol();
061
062    @Override
063    public String toString() {
064        if (expressions.size() == 2) {
065            return "( " + expressions.get(0) + " " + getExpressionSymbol() + " " + expressions.get(1) + " )";
066        }
067        StringBuilder result = new StringBuilder("(");
068        int count = 0;
069        for (BooleanExpression expression : expressions) {
070            if (count++ > 0) {
071                result.append(" " + getExpressionSymbol() + " ");
072            }
073            result.append(expression.toString());
074        }
075        result.append(")");
076        return result.toString();
077    }
078
079    public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) {
080        if (lvalue instanceof ORExpression) {
081            ORExpression orExpression = (ORExpression) lvalue;
082            orExpression.addExpression(rvalue);
083            return orExpression;
084        } else {
085            return new ORExpression(lvalue, rvalue);
086        }
087    }
088
089    public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) {
090        if (lvalue instanceof ANDExpression) {
091            ANDExpression orExpression = (ANDExpression) lvalue;
092            orExpression.addExpression(rvalue);
093            return orExpression;
094        } else {
095            return new ANDExpression(lvalue, rvalue);
096        }
097    }
098
099    @Override
100    public abstract Object evaluate(MessageEvaluationContext message) throws JMSException;
101
102    @Override
103    public abstract boolean matches(MessageEvaluationContext message) throws JMSException;
104
105    public static class ORExpression extends LogicExpression {
106
107        public ORExpression(BooleanExpression lvalue, BooleanExpression rvalue) {
108            super(lvalue, rvalue);
109        }
110
111        @Override
112        public Object evaluate(MessageEvaluationContext message) throws JMSException {
113            boolean someNulls = false;
114            for (BooleanExpression expression : expressions) {
115                Boolean lv = (Boolean)expression.evaluate(message);
116                if (lv != null && lv.booleanValue()) {
117                    return Boolean.TRUE;
118                }
119                if (lv == null) {
120                    someNulls = true;
121                }
122            }
123            if (someNulls) {
124                return null;
125            }
126            return Boolean.FALSE;
127        }
128
129        @Override
130        public boolean matches(MessageEvaluationContext message) throws JMSException {
131            for (BooleanExpression expression : expressions) {
132                boolean lv = expression.matches(message);
133                if (lv) {
134                    return true;
135                }
136            }
137            return false;
138        }
139
140        @Override
141        public String getExpressionSymbol() {
142            return "OR";
143        }
144    }
145
146    private static class ANDExpression extends LogicExpression {
147
148        public ANDExpression(BooleanExpression lvalue, BooleanExpression rvalue) {
149            super(lvalue, rvalue);
150        }
151
152        @Override
153        public Object evaluate(MessageEvaluationContext message) throws JMSException {
154            boolean someNulls = false;
155            for (BooleanExpression expression : expressions) {
156                Boolean lv = (Boolean)expression.evaluate(message);
157                if (lv != null && !lv.booleanValue()) {
158                    return Boolean.FALSE;
159                }
160                if (lv == null) {
161                    someNulls = true;
162                }
163            }
164            if (someNulls) {
165                return null;
166            }
167            return Boolean.TRUE;
168        }
169
170        @Override
171        public boolean matches(MessageEvaluationContext message) throws JMSException {
172            for (BooleanExpression expression : expressions) {
173                boolean lv = expression.matches(message);
174                if (!lv) {
175                    return false;
176                }
177            }
178            return true;
179        }
180
181        @Override
182        public String getExpressionSymbol() {
183            return "AND";
184        }
185    }
186}