View Javadoc

1   package net.sf.sapjcosupport;
2   
3   import org.apache.log4j.Logger;
4   import org.apache.oro.text.regex.*;
5   
6   import java.util.*;
7   
8   /**
9    * @author Niki Driessen
10   * @since Apr 21, 2006 - 10:47:03 AM
11   */
12  public class SapQuery {
13      private String searchHelpName, selectionMethod;
14      private int maximumResults;
15      private Class persistentObject;
16      private List javaFields = new ArrayList(2);
17      private List sapFields = new ArrayList(2);
18      private List criteria = new ArrayList(1);
19  
20      protected final Logger log = Logger.getLogger(getClass());
21      private static Map optionMap;
22      private static final String operatorRegex = "(like|not like|==|=|<>|!=|<=|>=|<|>)";
23  
24      static {
25          optionMap = new TreeMap();
26          optionMap.put("like", SearchCriterion.CONTAINS_PATTERN);
27          optionMap.put("not like", SearchCriterion.CONTAINS_NOT_PATTERN);
28          optionMap.put("==", SearchCriterion.EQUAL);
29          optionMap.put("=", SearchCriterion.EQUAL);
30          optionMap.put("<>", SearchCriterion.NOT_EQUAL);
31          optionMap.put("!=", SearchCriterion.NOT_EQUAL);
32          optionMap.put("<=", SearchCriterion.LESS_OR_EQUAL);
33          optionMap.put(">=", SearchCriterion.GREATER_OR_EQUAL);
34          optionMap.put("<", SearchCriterion.LESS);
35          optionMap.put(">", SearchCriterion.GREATER);
36  //        optionMap.put("between", SearchCriterion.BETWEEN);
37  //        optionMap.put("not between",SearchCriterion.NOT_BETWEEN);
38  
39      }
40  
41      private static final String WHERE = "where";
42      private static final String FROM = "from";
43      private static final String SELECT = "select";
44  
45      /**
46       * "partNumber = MATNR_EXT where MATNR_EXT like '?%' AND SPRAS = 'E'"
47       * "partNumber = MATNR_EXT, alternateDescription = MAKTG where MAKTG like '%?%' AND SPRAS = 'E'"
48       *
49       * @param query
50       */
51      public SapQuery(String query, Class persistentObject, int maximumResults) {
52          this.persistentObject = persistentObject;
53          this.maximumResults = maximumResults;
54          parseQuery(query);
55      }
56  
57      public class QueryParseException extends RuntimeException {
58          public QueryParseException(String queryPart, String expected, int offset) {
59              super("Syntax error near: " + queryPart + ", at position: " + offset + ", expected: " + expected);
60          }
61      }
62  
63      private class WhereClauseSplitter extends StringSplitter {
64          private Collection delims;
65  
66          public WhereClauseSplitter(String string) {
67              super(string, "(and|or)", false);
68          }
69  
70          public WhereClauseSplitter(String string, String regex) {
71              super(string, regex, false);
72          }
73  
74          protected void split() {
75              try {
76                  PatternCompiler compiler = new Perl5Compiler();
77                  Pattern pattern;
78                  if (caseSensitive) {
79                      pattern = compiler.compile(delim);
80                  } else {
81                      pattern = compiler.compile(delim, Perl5Compiler.CASE_INSENSITIVE_MASK);
82                  }
83                  PatternMatcher partsMatcher = new Perl5Matcher();
84                  parts = new ArrayList(5);
85                  delims = new ArrayList(5);
86                  int prev = 0;
87                  PatternMatcherInput input = new PatternMatcherInput(string);
88                  while (partsMatcher.contains(input, pattern)) {
89                      MatchResult result = partsMatcher.getMatch();
90                      parts.add(string.substring(prev, result.beginOffset(1)));
91                      delims.add(result.group(1));
92                      prev = result.endOffset(1) + 1;
93                      input.setBeginOffset(prev);
94                  }
95                  parts.add(string.substring(prev));
96              } catch (Exception e) {
97                  e.printStackTrace();
98                  throw new QueryParseException(string, "valid condition expression", 0);
99              }
100         }
101 
102         public Iterator iterator() {
103             return new WhereClauseIterator(parts.iterator(), delims.iterator());
104         }
105 
106         public class WhereClauseIterator implements Iterator {
107             private String matchedDelim;
108             private Iterator internal;
109             private Iterator delims;
110 
111             public WhereClauseIterator(Iterator parts, Iterator delims) {
112                 this.internal = parts;
113                 this.delims = delims;
114             }
115 
116             public void remove() {
117                 delims.remove();
118                 internal.remove();
119             }
120 
121             public boolean hasNext() {
122                 return internal.hasNext();
123             }
124 
125             public Object next() {
126                 if (delims.hasNext()) {
127                     matchedDelim = (String) delims.next();
128                 }
129                 return internal.next();
130             }
131 
132             public String getMatchedDelim() {
133                 return matchedDelim;
134             }
135         }
136 
137     }
138 
139     private class StringSplitter {
140         protected Collection parts;
141         protected String string;
142         protected String delim;
143         protected boolean caseSensitive;
144 
145         /**
146          * Splits a string by delim.
147          *
148          * @param string
149          * @param delim
150          */
151         public StringSplitter(String string, String delim, boolean caseSensitive) {
152             this.string = string;
153             this.delim = delim;
154             this.caseSensitive = caseSensitive;
155             split();
156         }
157 
158         protected void split() {
159             String source = this.string;
160             if (!caseSensitive) {
161                 source = string.toLowerCase();
162                 delim = delim.toLowerCase();
163             }
164             parts = new ArrayList();
165             int prev = 0;
166             int index = source.indexOf(delim);
167             while (index > 0) {
168                 String part = string.substring(prev, index);
169                 parts.add(part.trim());
170                 prev = index + delim.length() + 1;
171                 index = source.indexOf(delim, prev);
172             }
173             //last part...
174             parts.add(string.substring(prev).trim());
175         }
176 
177         public Iterator iterator() {
178             return parts.iterator();
179         }
180 
181         public int countParts() {
182             return parts.size();
183         }
184     }
185 
186     public void parseQuery(String query) {
187         //parse the query
188         StringSplitter whereSplitter = new StringSplitter(query, WHERE, false);
189         if (whereSplitter.countParts() != 2) {
190             throw new QueryParseException(query, WHERE, query.length());
191         }
192         Iterator mainParts = whereSplitter.iterator();
193         String temp = (String) mainParts.next();
194         StringSplitter selectFromSplitter = new StringSplitter(temp, FROM, false);
195         if (selectFromSplitter.countParts() != 2) {
196             throw new QueryParseException(temp, FROM, query.length());
197         }
198         Iterator selectFromParts = selectFromSplitter.iterator();
199         String selectClause = (String) selectFromParts.next();
200         selectClause = replaceFirst(selectClause, SELECT, "");
201         String fromClause = (String) selectFromParts.next();
202         String whereClause = (String) mainParts.next();
203 
204         StringTokenizer fieldSelectionTokenizer = new StringTokenizer(selectClause, ",");
205         while (fieldSelectionTokenizer.hasMoreTokens()) {
206             String part = fieldSelectionTokenizer.nextToken().trim();
207             parseFieldSelection(part, query.indexOf(part));
208         }
209 
210 
211         StringTokenizer fromTokenizer = new StringTokenizer(fromClause, ".");
212         if (fromTokenizer.countTokens() != 2) {
213             throw new QueryParseException(fromClause, "from SEARCHHELP.SELECTION_METHOD", fromClause.length());
214         }
215         searchHelpName = fromTokenizer.nextToken();
216         selectionMethod = fromTokenizer.nextToken();
217 
218         WhereClauseSplitter.WhereClauseIterator whereParts = (WhereClauseSplitter.WhereClauseIterator)
219                 new WhereClauseSplitter(whereClause).iterator();
220         while (whereParts.hasNext()) {
221             String part = (String) whereParts.next();
222             String delim = whereParts.getMatchedDelim();
223             parseCriteria(part);
224         }
225     }
226 
227     private String replaceFirst(String haystack, String needle, String replacement) {
228         int pos = haystack.indexOf(needle);
229         if (pos >= 0) {
230             return (pos > 0 ? haystack.substring(0, pos - 1) : "") +
231                     replacement +
232                     (((pos + needle.length()) < haystack.length()) ?
233                             haystack.substring(pos + needle.length()) : "");
234         }
235         return haystack;
236     }
237 
238     /**
239      * Parses expression of the form 'javaField = SAP_FIELD'.
240      * spaces are optional (can be none or more between the parts of the expression)
241      *
242      * @param queryPart the part of the query to interprete as a field selection
243      */
244     private void parseFieldSelection(String queryPart, int offset) {
245         StringTokenizer tokenizer = new StringTokenizer(queryPart, "=");
246         if (tokenizer.countTokens() != 2) {
247             throw new QueryParseException(queryPart, "=", offset);
248         }
249         String javaField = tokenizer.nextToken().trim();
250         String sapField = tokenizer.nextToken().trim();
251         javaFields.add(javaField);
252         sapFields.add(sapField);
253     }
254 
255     private void parseCriteria(String queryPart) {
256         WhereClauseSplitter splitter = new WhereClauseSplitter(queryPart, operatorRegex);
257         WhereClauseSplitter.WhereClauseIterator clauseParts = (WhereClauseSplitter.WhereClauseIterator) splitter.iterator();
258         if (splitter.countParts() != 2) {
259             throw new QueryParseException(queryPart, "'field [operator] value'", 0);
260         } else {
261             String field = ((String) clauseParts.next()).trim();
262             String option = (String) optionMap.get(clauseParts.getMatchedDelim());
263             String lowLimit = ((String) clauseParts.next()).trim();
264             if (field.length() == 0) {
265                 throw new QueryParseException(queryPart, "valid field name before operator", 0);
266             }
267             if (option == null) {
268                 throw new QueryParseException(queryPart, "valid operator", 0);
269             }
270             if (lowLimit.length() == 0) {
271                 throw new QueryParseException(queryPart, "valid expression after operator", 0);
272             }
273             if (lowLimit.charAt(0) == '\'') {
274                 lowLimit = lowLimit.substring(1);
275             }
276             if (lowLimit.charAt(lowLimit.length() - 1) == '\'') {
277                 lowLimit = lowLimit.substring(0, lowLimit.length() - 1);
278             }
279             lowLimit = lowLimit.replace('%', '*');
280             SearchCriterion criterion = new SearchCriterion();
281             criterion.setField(field);
282             //criterion.setHighLimit();
283             criterion.setLowLimit(lowLimit);
284             criterion.setIncluded(true);
285             criterion.setOption(option);
286             criteria.add(criterion);
287         }
288     }
289 
290     public void setParameter(String parameter) {
291         setParameter(0, parameter);
292     }
293 
294     public void setParameter(int position, String parameter) {
295         SearchCriterion criterion = (SearchCriterion) criteria.get(position);
296         if (criterion != null) {
297             String lowLimit = criterion.getLowLimit();
298             lowLimit = replaceFirst(lowLimit, "?", parameter);
299             criterion.setLowLimit(lowLimit);
300         } else {
301             throw new IndexOutOfBoundsException("No parameter at position " + position);
302         }
303     }
304 
305     public void setParameters(String[] params) {
306         for (int i = 0; i < params.length; i++) {
307             String param = params[i];
308             setParameter(i, param);
309         }
310     }
311 
312     public List executeQuery(SapSearchHelp searchHelper) {
313         return searchHelper.search(searchHelpName, selectionMethod, maximumResults, criteria, persistentObject, javaFields, sapFields);
314     }
315 }