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 }