001 package railo.runtime.search.lucene2.query; 002 003 import java.util.ArrayList; 004 import java.util.Iterator; 005 import java.util.List; 006 007 import railo.commons.lang.ParserString; 008 009 010 /** 011 * The simple query is the default query type and is appropriate for the vast majority of searches. 012 * When entering text on a search form, you perform a simple query by entering a word or comma-delimited strings, 013 * with optional wildcard characters. 014 * Verity treats each comma as a logical OR. If you omit the commas, Verity treats the expression as a phrase. 015 */ 016 public final class QueryParser { 017 private static final String OR="or"; 018 private static final String AND="and"; 019 private static final String NOT="not"; 020 private static final char QUOTER='"'; 021 022 private static final String STAR = "*"; 023 private List list=new ArrayList(); 024 025 026 /** 027 * parse given string query 028 * @param criteria 029 * @return matching Query 030 */ 031 public String parse(String criteria) { 032 Op op = parseOp(criteria); 033 if(op==null) return STAR; 034 return op.toString(); 035 } 036 public Op parseOp(String criteria) { 037 if(criteria.length()>0) { 038 char first=criteria.charAt(0); 039 // start with operator 040 while(first=='*' || first=='~' || first=='?') { 041 criteria=criteria.substring(1); 042 if(criteria.length()==0) break; 043 first=criteria.charAt(0); 044 } 045 } 046 047 // make never foud query if quey is empty 048 if(criteria.length()==0) { 049 return null; 050 } 051 052 //StringBuffer str=new StringBuffer(); 053 ParserString ps=new ParserString(criteria); 054 Op op=null; 055 while(!ps.isAfterLast()) { 056 if(op==null)op=orOp(ps); 057 else op=new Concator(op,orOp(ps)); 058 } 059 return op; 060 } 061 062 063 064 private Op orOp(ParserString ps) { 065 Op op=andOp(ps); 066 ps.removeSpace(); 067 068 // OR 069 while(ps.isValidIndex() && (ps.forwardIfCurrent(OR) || ps.forwardIfCurrent(','))) { 070 ps.removeSpace(); 071 if(ps.isAfterLast()) op=new Concator(op,new Literal("OR") ); 072 else op=new Or(op,andOp(ps)); 073 } 074 return op; 075 } 076 077 private Op andOp(ParserString ps) { 078 Op op = notOp(ps); 079 ps.removeSpace(); 080 081 // AND 082 while(ps.isValidIndex() && ps.forwardIfCurrent(AND)) { 083 ps.removeSpace(); 084 if(ps.isAfterLast()) op=new Concator(op,new Literal("AND") ); 085 else op=new And(op,notOp(ps)); 086 } 087 return op; 088 } 089 090 private Op notOp(ParserString ps) { 091 Op op = spaceOp(ps); 092 ps.removeSpace(); 093 094 // NOT 095 while(ps.isValidIndex() && ps.forwardIfCurrent(NOT)) { 096 ps.removeSpace(); 097 if(ps.isAfterLast()) op=new Concator(op,new Literal("NOT") ); 098 else { 099 Op r; 100 op=new Not(op,r=clip(ps)); 101 this.list.remove(r); 102 } 103 } 104 return op; 105 } 106 107 private Op spaceOp(ParserString ps) { 108 Op op = clip(ps); 109 //ps.removeSpace(); 110 111 // Concat 112 while(ps.isValidIndex() && isSpace(ps.getCurrent()) && !(ps.isCurrentIgnoreSpace(OR) || ps.isCurrentIgnoreSpace(',') || ps.isCurrentIgnoreSpace(AND) || ps.isCurrentIgnoreSpace(NOT))) { 113 ps.removeSpace(); 114 op=new Concator(op,clip(ps)); 115 } 116 return op; 117 } 118 119 private Op clip(ParserString ps) { 120 // () 121 if(ps.isValidIndex() && ps.forwardIfCurrent('(')) { 122 Op op=orOp(ps); 123 ps.removeSpace(); 124 ps.forwardIfCurrent(')'); 125 ps.removeSpace(); 126 return op; 127 } 128 return literal(ps); 129 } 130 131 private Op literal(ParserString ps) { 132 ps.removeSpace(); 133 134 if(ps.isCurrent(QUOTER)) return quotedLiteral(ps); 135 return notQuotedLiteral(ps); 136 } 137 138 139 private Op quotedLiteral(ParserString ps) { 140 StringBuffer str=new StringBuffer(); 141 ps.next(); 142 char c; 143 while(!ps.isAfterLast()) { 144 c=ps.getCurrent(); 145 if(c==QUOTER) { 146 ps.next(); 147 if(ps.isCurrent(QUOTER)) str.append(QUOTER); 148 else break; 149 } 150 else { 151 str.append(c); 152 } 153 ps.next(); 154 } 155 156 return register(new Literal(str.toString())); 157 } 158 159 private Op notQuotedLiteral(ParserString ps) { 160 161 StringBuffer str=new StringBuffer(); 162 ps.removeSpace(); 163 164 char c; 165 166 while(!ps.isAfterLast()) { 167 c=ps.getCurrent(); 168 if(isSpace(c) || c==',') break; 169 str.append(c); 170 ps.next(); 171 } 172 return register(new Literal(str.toString())); 173 174 } 175 176 private boolean isSpace(char c) { 177 return c==' ' || c=='\t' || c=='\n' || c=='\b'; 178 } 179 180 181 182 /*public static void main(String[] args) { 183 QueryParser qp = new QueryParser(); 184 185 qp.parseOp("aaa zzz not bbb and ccc"); 186 print.out(qp.getLiteralSearchedTerms()); 187 if(true) return; 188 print.out(qp.parse("\"abc\"")); 189 print.out(qp.parse("abc")); 190 print.out(qp.parse("abc def")); 191 print.out(qp.parse("abc def")); 192 print.out(qp.parse("abc and def")); 193 print.out(qp.parse("\"\"\"abc\"\"\"")); 194 print.out(qp.parse("\"abc\" susi or peter")); 195 print.out(qp.parse("abc susi or peter")); 196 print.out(qp.parse("abc or susi or peter")); 197 print.out(qp.parse("*abc susi and peter or \"abc\"* , xxx,yy*")); 198 print.out(qp.parse("xxx,y\"y*")); 199 print.out(qp.parse("xxx y\"y*")); 200 print.out(qp.parse("")); 201 print.out(qp.parse("per or")); 202 print.out(qp.parse("per and")); 203 print.out(qp.parse("per not")); 204 print.out(qp.parse("andi per not susi")); 205 print.out(qp.parse("\"kinderhort test\"")); 206 }*/ 207 public Literal register(Literal literal) { 208 list.add(literal); 209 return literal; 210 } 211 212 public Literal[] getLiteralSearchedTerms() { 213 return (Literal[]) list.toArray(new Literal[list.size()]); 214 } 215 216 public String[] getStringSearchedTerms() { 217 Iterator it = list.iterator(); 218 String[] rtn=new String[list.size()]; 219 int i=0; 220 while(it.hasNext()) { 221 rtn[i++]=it.next().toString(); 222 } 223 224 return rtn; 225 } 226 227 }