001    /*
002     * 
003     * $Revision: 14796 $ $Date: 2009-03-02 16:43:18 +0100 (Mon, 02 Mar 2009) $
004     *
005     * This file is part of ***  M y C o R e  ***
006     * See http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, in a file called gpl.txt or license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     */
023    
024    package org.mycore.parsers.bool;
025    
026    import java.util.ArrayList;
027    import java.util.List;
028    import java.util.regex.Matcher;
029    import java.util.regex.Pattern;
030    
031    import org.jdom.Element;
032    
033    /**
034     * Class for parsing Boolean clauses
035     * 
036     * @author Matthias Kramm
037     */
038    public class MCRBooleanClauseParser {
039        private static Pattern bracket = Pattern.compile("\\([^)(]*\\)");
040        
041        private static Pattern and = Pattern.compile("[)\\s]+[aA][nN][dD][\\s(]+");
042    
043        private static Pattern or = Pattern.compile("[)\\s]+[oO][rR][\\s(]+");
044    
045        private static Pattern marker = Pattern.compile("@<([0-9]*)>@");
046    
047        private static String extendClauses(String s, List l) {
048            while (true) {
049                Matcher m = marker.matcher(s);
050    
051                if (m.find()) {
052                    String c = m.group();
053                    String clause = (String) l.get(Integer.parseInt(m.group(1)));
054                    s = s.replaceAll(c, clause);
055                } else {
056                    break;
057                }
058            }
059    
060            return s;
061        }
062    
063        public MCRCondition parse(Element condition) {
064            if (condition == null) {
065                return defaultRule();
066            }
067    
068            if (condition.getName().toLowerCase().equals("boolean")) {
069                String name = condition.getAttributeValue("operator").toLowerCase();
070    
071                if (name.equals("not")) {
072                    Element child = (Element) (condition.getChildren().get(0));
073                    return new MCRNotCondition(parse(child));
074                } else if (name.equals("and") || name.equals("or")) {
075                    List children = condition.getChildren();
076                    MCRCondition cond;
077    
078                    if (name.equals("and")) {
079                        MCRAndCondition acond = new MCRAndCondition();
080    
081                        for (int i = 0; i < children.size(); i++) {
082                            Element child = (Element) (children.get(i));
083                            acond.addChild(parse(child));
084                        }
085    
086                        cond = acond;
087                    } else {
088                        MCROrCondition ocond = new MCROrCondition();
089    
090                        for (int i = 0; i < children.size(); i++) {
091                            Element child = (Element) (children.get(i));
092                            ocond.addChild(parse(child));
093                        }
094    
095                        cond = ocond;
096                    }
097    
098                    return cond;
099                } else {
100                    return parseSimpleCondition(condition);
101                }           
102            }
103            return parseSimpleCondition(condition);
104        }
105    
106        public MCRCondition parse(String s) throws MCRParseException {
107            s = s.replaceAll("\t", " ").replaceAll("\n", " ").replaceAll("\r", " ");
108    
109            if ((s.trim().length() == 0) || s.equals("()")) {
110                return defaultRule();
111            }
112    
113            return parse(s, null);
114        }
115    
116        private MCRCondition parse(String s, List l) throws MCRParseException {
117            if (l == null) {
118                l = new ArrayList();
119            }
120    
121            s = s.trim();
122            if (s.equals("()")) { s = "(true)"; }
123    
124            /* replace all bracket expressions with $n */
125            while (true) {
126                /* remove outer brackets () */
127                while ((s.charAt(0) == '(') && (s.charAt(s.length() - 1) == ')') && (s.substring(1, s.length() - 1).indexOf('(') < 0) && (s.substring(1, s.length() - 1).indexOf(')') < 0)) {
128                    s = s.substring(1, s.length() - 1).trim();
129                }
130    
131                Matcher m = bracket.matcher(s);
132    
133                if (m.find()) {
134                    String clause = m.group();
135                    s = s.substring(0, m.start()) + "@<" + l.size() + ">@" + s.substring(m.end());
136                    l.add(extendClauses(clause, l));
137                } else {
138                    break;
139                }
140            }
141    
142            /* handle OR */
143            Matcher m = or.matcher(s);
144            int last = 0;
145            MCROrCondition orclause = new MCROrCondition();
146            while(m.find()) {
147                int l1 = m.start();
148                if (last >= l1) {
149                    throw new MCRParseException("subclause of OR missing while parsing \"" + s + "\"");
150                }
151                MCRCondition c = parse(extendClauses(s.substring(last, l1), l) , l);
152                last = m.end();
153                orclause.addChild(c);
154            }
155            if(last!=0) {
156                MCRCondition c = parse(extendClauses(s.substring(last), l) , l);
157                orclause.addChild(c);
158                return orclause;
159            }
160    
161            /* handle AND */
162            m = and.matcher(s);
163            last = 0;
164            MCRAndCondition andclause = new MCRAndCondition();
165            while(m.find()) {
166                int l1 = m.start();
167                if (last >= l1) {
168                    throw new MCRParseException("subclause of AND missing while parsing \"" + s + "\"");
169                }
170                MCRCondition c = parse(extendClauses(s.substring(last, l1), l) , l);
171                last = m.end();
172                andclause.addChild(c);
173            }
174            if(last!=0) {
175                MCRCondition c = parse(extendClauses(s.substring(last), l) , l);
176                andclause.addChild(c);
177                return andclause;
178            }
179    
180            /* handle NOT */
181            s = s.trim();
182    
183            if (s.toLowerCase().startsWith("not ")) {
184                MCRCondition inverse = parse(extendClauses(s.substring(4), l), l);
185    
186                return new MCRNotCondition(inverse);
187            }
188    
189            s = extendClauses(s,l);
190            if(s.indexOf('(') >= 0)
191                return parse(s,l);
192            else
193                return parseSimpleCondition(s);
194        }
195    
196        protected MCRCondition parseSimpleCondition(String s) throws MCRParseException {
197            /* handle specific rules */
198            s = s.toLowerCase();
199            if (s.equalsIgnoreCase("true")) {
200                return new MCRTrueCondition();
201            }
202    
203            if (s.equalsIgnoreCase("false")) {
204                return new MCRFalseCondition();
205            }
206    
207            throw new MCRParseException("syntax error: " + s); // extendClauses(s,
208                                                                // l));
209        }
210    
211        protected MCRCondition parseSimpleCondition(Element e) throws MCRParseException {
212            // <boolean operator="true|false" />
213            String name = e.getAttributeValue("operator").toLowerCase();
214    
215            if (name.equals("true")) {
216                return new MCRTrueCondition();
217            }
218    
219            if (name.equals("false")) {
220                return new MCRFalseCondition();
221            }
222    
223            throw new MCRParseException("syntax error: <" + name + ">");
224        }
225    
226        protected MCRCondition defaultRule() {
227            return new MCRTrueCondition();
228        }
229    
230        public static void main(String[] args)
231        {
232            MCRBooleanClauseParser p = new MCRBooleanClauseParser();
233            System.out.println(p.parse("true or false or true").toString());
234            System.out.println(p.parse("(true) or (false) or (true)").toString());
235            System.out.println(p.parse("true and false and true").toString());
236            System.out.println(p.parse("true or false and true").toString());
237            System.out.println(p.parse("true or true or (true or true)").toString());
238            System.out.println(p.parse("((true))").toString());
239            System.out.println(p.parse("(true ) or  ( ((false) or (true)))").toString());
240        }
241    }