OSDN Git Service

Added the ability to search for phrases
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / sql / REnSearch.java
1 /*\r
2  * This file is part of NeverNote \r
3  * Copyright 2009 Randy Baumgarte\r
4  * \r
5  * This file may be licensed under the terms of of the\r
6  * GNU General Public License Version 2 (the ``GPL'').\r
7  *\r
8  * Software distributed under the License is distributed\r
9  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either\r
10  * express or implied. See the GPL for the specific language\r
11  * governing rights and limitations.\r
12  *\r
13  * You should have received a copy of the GPL along with this\r
14  * program. If not, go to http://www.gnu.org/licenses/gpl.html\r
15  * or write to the Free Software Foundation, Inc.,\r
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
17  *\r
18 */\r
19 \r
20 \r
21 package cx.fbn.nevernote.sql;\r
22 \r
23 import java.text.SimpleDateFormat;\r
24 import java.util.ArrayList;\r
25 import java.util.Calendar;\r
26 import java.util.GregorianCalendar;\r
27 import java.util.List;\r
28 import java.util.regex.Pattern;\r
29 \r
30 import org.apache.commons.lang.StringEscapeUtils;\r
31 \r
32 import com.evernote.edam.type.Note;\r
33 import com.evernote.edam.type.Notebook;\r
34 import com.evernote.edam.type.Tag;\r
35 \r
36 import cx.fbn.nevernote.sql.driver.NSqlQuery;\r
37 import cx.fbn.nevernote.utilities.ApplicationLogger;\r
38 \r
39 public class REnSearch {\r
40         \r
41         private final List<String>      searchWords;\r
42         private final List<String>  searchPhrases;\r
43         private final List<String>      notebooks;\r
44         private final List<String>      tags;\r
45         private final List<String>      intitle;\r
46         private final List<String>      created;\r
47         private final List<String>      updated;\r
48         private final List<String>      resource;\r
49         private final List<String>      subjectDate;\r
50         private final List<String>      longitude;\r
51         private final List<String>      latitude;\r
52         private final List<String>      altitude;\r
53         private final List<String>      author;\r
54         private final List<String>      source;\r
55         private final List<String>      sourceApplication;\r
56         private final List<String>      recoType;\r
57         private final List<String>      todo;\r
58         private final List<Tag>         tagIndex;\r
59         private final ApplicationLogger logger;\r
60 //      private final DatabaseConnection db;\r
61         private boolean any;\r
62         private int     minimumWordLength = 3;\r
63         private int minimumRecognitionWeight = 80;\r
64         private final DatabaseConnection conn;\r
65         \r
66         public REnSearch(DatabaseConnection c, ApplicationLogger l, String s, List<Tag> t, int m, int r) {\r
67                 logger = l;\r
68                 conn = c;\r
69                 tagIndex = t;\r
70                 minimumWordLength = m;\r
71                 minimumRecognitionWeight = r;\r
72                 searchWords = new ArrayList<String>();\r
73                 searchPhrases = new ArrayList<String>();\r
74                 notebooks = new ArrayList<String>();\r
75                 tags = new ArrayList<String>();\r
76                 intitle = new ArrayList<String>();\r
77                 created = new  ArrayList<String>();\r
78                 updated = new ArrayList<String>();\r
79                 resource = new ArrayList<String>();\r
80                 subjectDate = new ArrayList<String>();\r
81                 longitude = new ArrayList<String>();\r
82                 latitude = new ArrayList<String>();\r
83                 altitude = new ArrayList<String>();\r
84                 author = new ArrayList<String>();\r
85                 source = new ArrayList<String>();\r
86                 sourceApplication = new ArrayList<String>();\r
87                 recoType = new ArrayList<String>();\r
88                 todo = new ArrayList<String>();\r
89                 any = false;\r
90                 \r
91                 if (s == null) \r
92                         return;\r
93                 if (s.trim().equals(""))\r
94                         return;\r
95                 \r
96                 resolveSearch(s);\r
97         }\r
98                 \r
99         public List<String> getWords() { return searchWords; }\r
100         public List<String> getNotebooks() { return notebooks; }\r
101         public List<String> getIntitle() {      return intitle; }\r
102         public List<String> getTags() { return tags; }\r
103         public List<String> getResource() {     return resource; }\r
104         public List<String> getAuthor() { return author; }      \r
105         public List<String> getSource() { return source; }      \r
106         public List<String> getSourceApplication() { return sourceApplication; }        \r
107         public List<String> getRecoType() {     return recoType; }      \r
108         public List<String> getToDo() { return todo; }\r
109         public List<String> getLongitude() { return longitude; }\r
110         public List<String> getLatitude() { return latitude; }\r
111         public List<String> getAltitude() { return altitude; }\r
112         public List<String> getCreated() { return created; }\r
113         public List<String> getUpdated() { return updated; }\r
114         public List<String> getSubjectDate() { return subjectDate; }\r
115         \r
116 \r
117         // match tag names\r
118         private boolean matchTagsAll(List<String> tagNames) {\r
119                 List<String> list = getTags();\r
120                                 \r
121                 for (int j=0; j<list.size(); j++) {\r
122                         boolean negative = false;\r
123                         negative = false;\r
124                         if (list.get(j).startsWith("-"))\r
125                                 negative = true;\r
126                         int pos = list.get(j).indexOf(":");\r
127                         String filterName = cleanupWord(list.get(j).substring(pos+1));\r
128                         filterName = filterName.replace("*", ".*");   // setup for regular expression pattern match\r
129                         \r
130                         if (tagNames.size() == 0 && !negative)\r
131                                 return false;\r
132                         if (tagNames.size() == 0 && negative)\r
133                                 return true;\r
134                         \r
135                         for (int i=0; i<tagNames.size(); i++) {         \r
136                                 boolean matches = Pattern.matches(filterName.toLowerCase(),tagNames.get(i).toLowerCase());\r
137                                 if (matches && negative)\r
138                                         return false;\r
139                                 if (!matches && !negative)\r
140                                         return false;\r
141                         }\r
142                 }\r
143                 return true;\r
144         }\r
145         \r
146         // match tag names\r
147         private boolean matchTagsAny(List<String> tagNames) {\r
148                 List<String> list = getTags();\r
149                 if (list.size() == 0)\r
150                         return true;\r
151                 \r
152                 boolean negative = false;               \r
153                 boolean found = false;\r
154                 \r
155                 for (int j=0; j<list.size(); j++) {\r
156                         negative = false;\r
157                         if (list.get(j).startsWith("-"))\r
158                                 negative = true;\r
159                         int pos = list.get(j).indexOf(":");\r
160                         String filterName = cleanupWord(list.get(j).substring(pos+1));\r
161                         filterName = filterName.replace("*", ".*");   // setup for regular expression pattern match\r
162                         \r
163                         if (tagNames.size() == 0)\r
164                                 found = false;\r
165 \r
166                         for (int i=0; i<tagNames.size(); i++) {         \r
167                                 boolean matches = Pattern.matches(filterName.toLowerCase(),tagNames.get(i).toLowerCase());\r
168                                 if (matches)\r
169                                         found = true;\r
170                         }\r
171                 }\r
172                 if (negative)\r
173                         return !found;\r
174                 else\r
175                         return found;\r
176         }\r
177         \r
178         // Match notebooks in search terms against notes\r
179         private boolean matchNotebook(String guid) {\r
180                 if (getNotebooks().size() == 0)\r
181                         return true;\r
182                 NotebookTable bookTable = new NotebookTable(logger, conn);\r
183                 List<Notebook> books = bookTable.getAll();\r
184 \r
185                 String name = new String("");\r
186                 for (int i=0; i<books.size(); i++) {\r
187                         if (guid.equalsIgnoreCase(books.get(i).getGuid())) {\r
188                                 name = books.get(i).getName();\r
189                                 i=books.size();\r
190                         }\r
191                 }\r
192                 if (any)\r
193                         return matchListAny(getNotebooks(), name);\r
194                 else\r
195                         return matchListAll(getNotebooks(), name);\r
196         }\r
197         // Match notebooks in search terms against notes\r
198         private boolean matchListAny(List<String> list, String title) {\r
199                 if (list.size() == 0)\r
200                         return true;\r
201                 boolean negative = false;\r
202                 boolean found = false;\r
203                 for (int i=0; i<list.size(); i++) {\r
204                         int pos = list.get(i).indexOf(":");\r
205                         negative = false;\r
206                         if (list.get(i).startsWith("-"))\r
207                                 negative = true;\r
208                         String filterName = cleanupWord(list.get(i).substring(pos+1));\r
209                         filterName = filterName.replace("*", ".*");   // setup for regular expression pattern match\r
210                         boolean matches = Pattern.matches(filterName.toLowerCase(),title.toLowerCase());\r
211                         if (matches)\r
212                                 found = true;\r
213                 }\r
214                 if (negative)\r
215                         return !found;\r
216                 else\r
217                         return found;\r
218         }\r
219         // Match notebooks in search terms against notes\r
220         private boolean matchContentAny(Note n) {\r
221                 if (todo.size() == 0 && resource.size() == 0 && searchPhrases.size() == 0)\r
222                         return true;\r
223 \r
224                 // pull back the record\r
225                 n = conn.getNoteTable().getNote(n.getGuid(), true, true, false, false, false);\r
226 \r
227                 // Check for search phrases\r
228                 String text = StringEscapeUtils.unescapeHtml(n.getContent().replaceAll("\\<.*?\\>", "")).toLowerCase();\r
229                 boolean negative = false;\r
230                 for (int i=0; i<searchPhrases.size(); i++) {\r
231                         String phrase = searchPhrases.get(i);\r
232                         if (phrase.startsWith("-")) {\r
233                                 negative = true;\r
234                                 phrase = phrase.substring(1);\r
235                         } else\r
236                                 negative = false;\r
237                         phrase = phrase.substring(1);\r
238                         phrase = phrase.substring(0,phrase.length()-1);\r
239                         System.out.println(phrase);\r
240                         if (text.indexOf(phrase)>=0) {\r
241                                 if (negative)\r
242                                         return false;\r
243                                 else\r
244                                         return true;\r
245                         }\r
246                         if (text.indexOf(phrase)<0 && negative)\r
247                                 return true;\r
248                 }\r
249                 \r
250                 for (int i=0; i<todo.size(); i++) {\r
251                         String value = todo.get(i);\r
252                         value = value.replace("\"", "");\r
253                         boolean desiredState;\r
254                         if (!value.endsWith(":false") && !value.endsWith(":true") && !value.endsWith(":*") && !value.endsWith("*"))\r
255                                 return false;\r
256                         if (value.endsWith(":false"))\r
257                                 desiredState = false;\r
258                         else\r
259                                 desiredState = true;\r
260                         if (value.startsWith("-"))\r
261                                 desiredState = !desiredState;\r
262                         int pos = n.getContent().indexOf("<en-todo");\r
263                         if (pos == -1 && value.startsWith("-") && (value.endsWith("*") || value.endsWith(":")))\r
264                                 return true;\r
265                         if (value.endsWith("*"))\r
266                                 return true;\r
267                         while (pos > -1) {\r
268                                 int endPos = n.getContent().indexOf("/>", pos);\r
269                                 String segment = n.getContent().substring(pos, endPos);\r
270                                 boolean currentState;\r
271                                 if (segment.toLowerCase().indexOf("checked=\"true\"") == -1)\r
272                                         currentState = false;\r
273                                 else\r
274                                         currentState = true;\r
275                                 if (desiredState == currentState)\r
276                                         return true;\r
277                                 \r
278                                 pos = n.getContent().indexOf("<en-todo", pos+1);\r
279                         }\r
280                 }\r
281                 \r
282                 // Check resources\r
283                 for (int i=0; i<resource.size(); i++) {\r
284                         String resourceString = resource.get(i);\r
285                         resourceString = resourceString.replace("\"", "");\r
286                         if (resourceString.startsWith("-"))\r
287                                 negative = true;\r
288                         resourceString = resourceString.substring(resourceString.indexOf(":")+1);\r
289                         for (int j=0; j<n.getResourcesSize(); j++) {\r
290                                 boolean match = stringMatch(n.getResources().get(j).getMime(), resourceString, negative);\r
291                                 if (match)\r
292                                         return true;\r
293                         }\r
294                 }\r
295                 return false;\r
296         }\r
297         \r
298         \r
299         // Take the initial search & split it apart\r
300         private void resolveSearch(String search) {\r
301                 List<String> words = new ArrayList<String>();\r
302                 StringBuffer b = new StringBuffer(search);\r
303                 \r
304                 int len = search.length();\r
305                 char nextChar = ' ';\r
306                 boolean quote = false;\r
307                 for (int i=0; i<len; i++) {\r
308                         if (search.charAt(i)==nextChar && !quote) {\r
309                                 b.setCharAt(i,'\0');\r
310                                 nextChar = ' ';\r
311                         } else {\r
312                                 if (search.charAt(i)=='\"') {\r
313                                         if (!quote) {\r
314                                                 quote=true;\r
315                                         } else {\r
316                                                 quote=false;\r
317                                         }\r
318                                 }\r
319                         }\r
320                         if (((i+2)<len) && search.charAt(i) == '\\') {\r
321                                 i=i+2;\r
322                         }\r
323                 }\r
324                 \r
325                 search = b.toString();\r
326                 int pos = 0;\r
327                 for (int i=0; i<search.length(); i++) {\r
328                         if (search.charAt(i) == '\0') {\r
329                                 search = search.substring(1);\r
330                                 i=0;\r
331                         } else {\r
332                                 pos = search.indexOf('\0');\r
333                                 if (pos > 0) {\r
334                                         words.add(search.substring(0,pos).toLowerCase());\r
335                                         search = search.substring(pos);\r
336                                         i=0;\r
337                                 }\r
338                         }\r
339                 }\r
340                 if (search.charAt(0)=='\0')     \r
341                         words.add(search.substring(1).toLowerCase());\r
342                 else\r
343                         words.add(search.toLowerCase());\r
344                 parseTerms(words);\r
345         }\r
346 \r
347         \r
348         // Parse out individual words into separate lists\r
349         // Supported options\r
350         // Tags\r
351         // Notebooks\r
352         // Intitle\r
353         // author\r
354         // source\r
355         // source application\r
356         // created\r
357         // updated\r
358         // subject date\r
359 \r
360         private void parseTerms(List<String> words) {\r
361                 int minLen = minimumWordLength;\r
362                 \r
363                 for (int i=0; i<words.size(); i++) {\r
364                         String word = words.get(i);\r
365                         int pos = word.indexOf(":");\r
366                         if (word.startsWith("any:")) {\r
367                                 any = true;\r
368                                 word = word.substring(4).trim();\r
369                                 pos = word.indexOf(":");\r
370                         }\r
371                         boolean searchPhrase = false;\r
372                         if (pos < 0 && word.indexOf(" ") > 0) {\r
373                                 searchPhrase=true;\r
374                                 searchPhrases.add(word.toLowerCase());\r
375                         }\r
376                         if (!searchPhrase && pos < 0 && (word.length() >= minLen || word.indexOf('*')>=0)) \r
377                                 getWords().add(word);\r
378                         if (word.startsWith("intitle:")) \r
379                                 intitle.add("*"+word+"*");\r
380                         if (word.startsWith("-intitle:")) \r
381                                 intitle.add("*"+word+"*");\r
382                         if (word.startsWith("notebook:")) \r
383                                 notebooks.add(word);\r
384                         if (word.startsWith("-notebook:")) \r
385                                 notebooks.add(word);\r
386                         if (word.startsWith("tag:")) \r
387                                 tags.add(word);\r
388                         if (word.startsWith("-tag:")) \r
389                                 tags.add(word);\r
390                         if (word.startsWith("resource:")) \r
391                                 resource.add(word);\r
392                         if (word.startsWith("-resource:")) \r
393                                 resource.add(word);\r
394                         if (word.startsWith("author:")) \r
395                                 author.add(word);\r
396                         if (word.startsWith("-author:")) \r
397                                 author.add(word);\r
398                         if (word.startsWith("source:")) \r
399                                 source.add(word);\r
400                         if (word.startsWith("-source:")) \r
401                                 source.add(word);\r
402                         if (word.startsWith("sourceapplication:")) \r
403                                 sourceApplication.add(word);\r
404                         if (word.startsWith("-sourceapplication:")) \r
405                                 sourceApplication.add(word);\r
406                         if (word.startsWith("recotype:")) \r
407                                 recoType.add(word);\r
408                         if (word.startsWith("-recotype:")) \r
409                                 recoType.add(word);\r
410                         if (word.startsWith("todo:")) \r
411                                 todo.add(word);\r
412                         if (word.startsWith("-todo:")) \r
413                                 todo.add(word);\r
414 \r
415                         if (word.startsWith("latitude:")) \r
416                                 latitude.add(word);\r
417                         if (word.startsWith("-latitude:")) \r
418                                 latitude.add(word);\r
419                         if (word.startsWith("longitude:")) \r
420                                 longitude.add(word);\r
421                         if (word.startsWith("-longitude:")) \r
422                                 longitude.add(word);\r
423                         if (word.startsWith("altitude:")) \r
424                                 altitude.add(word);\r
425                         if (word.startsWith("-altitude:")) \r
426                                 altitude.add(word);\r
427 \r
428                         if (word.startsWith("created:")) \r
429                                 created.add(word);\r
430                         if (word.startsWith("-created:")) \r
431                                 created.add(word);\r
432                         if (word.startsWith("updated:")) \r
433                                 updated.add(word);\r
434                         if (word.startsWith("-updated:")) \r
435                                 updated.add(word);\r
436                         if (word.startsWith("subjectdate:")) \r
437                                 created.add(word);\r
438                         if (word.startsWith("-subjectdate:")) \r
439                                 created.add(word);\r
440 \r
441                 }\r
442         }\r
443         // Match notebooks in search terms against notes\r
444         private boolean matchListAll(List<String> list, String title) {\r
445                 if (list.size() == 0)\r
446                         return true;\r
447                 boolean negative = false;\r
448                 for (int i=0; i<list.size(); i++) {\r
449                         int pos = list.get(i).indexOf(":");\r
450                         negative = false;\r
451                         if (list.get(i).startsWith("-"))\r
452                                 negative = true;\r
453                         String filterName = cleanupWord(list.get(i).substring(pos+1));\r
454                         filterName = filterName.replace("*", ".*");   // setup for regular expression pattern match\r
455                         boolean matches = Pattern.matches(filterName.toLowerCase(),title.toLowerCase());\r
456                         if (matches && negative)\r
457                                 return false;\r
458                         if (matches && !negative)\r
459                                 return true;\r
460                 }\r
461                 if (negative)\r
462                         return true;\r
463                 else\r
464                         return false;\r
465         }\r
466         // Match notebooks in search terms against notes\r
467         private boolean matchContentAll(Note n) {\r
468                 if (todo.size() == 0 && resource.size() == 0 && searchPhrases.size() == 0)\r
469                         return true;\r
470                 \r
471                 boolean returnTodo = false;\r
472                 boolean returnResource = false;\r
473                 boolean returnPhrase = false;\r
474                 \r
475                 if (todo.size() == 0)\r
476                         returnTodo = true;\r
477                 if (resource.size() == 0)\r
478                         returnResource = true;\r
479                 if (searchPhrases.size() == 0)\r
480                         returnPhrase = true;\r
481                 \r
482                 \r
483                 n = conn.getNoteTable().getNote(n.getGuid(), true, true, false, false, false);\r
484                 \r
485                 // Check for search phrases\r
486                 String text = StringEscapeUtils.unescapeHtml(n.getContent().replaceAll("\\<.*?\\>", "")).toLowerCase();\r
487                 boolean negative = false;\r
488                 for (int i=0; i<searchPhrases.size(); i++) {\r
489                         String phrase = searchPhrases.get(i);\r
490                         if (phrase.startsWith("-")) {\r
491                                 negative = true;\r
492                                 phrase = phrase.substring(1);\r
493                         } else\r
494                                 negative = false;\r
495                         phrase = phrase.substring(1);\r
496                         phrase = phrase.substring(0,phrase.length()-1);\r
497                         System.out.println(phrase);\r
498                         if (text.indexOf(phrase)>=0) {\r
499                                 if (!negative)\r
500                                         returnPhrase = true;\r
501                         }\r
502                         if (text.indexOf(phrase)<0 && negative)\r
503                                 returnPhrase = true;\r
504                 }\r
505 \r
506                 \r
507                 for (int i=0; i<todo.size(); i++) {\r
508                         String value = todo.get(i);\r
509                         value = value.replace("\"", "");\r
510                         boolean desiredState;\r
511                         if (!value.endsWith(":false") && !value.endsWith(":true") && !value.endsWith(":*") && !value.endsWith("*"))\r
512                                 return false;\r
513                         if (value.endsWith(":false"))\r
514                                 desiredState = false;\r
515                         else\r
516                                 desiredState = true;\r
517                         if (value.startsWith("-"))\r
518                                 desiredState = !desiredState;\r
519                         int pos = n.getContent().indexOf("<en-todo");\r
520                         if (pos == -1 && value.startsWith("-") && (value.endsWith("*") || value.endsWith(":")))\r
521                                 return true;\r
522                         if (pos > -1 && value.startsWith("-") && (value.endsWith("*") || value.endsWith(":")))\r
523                                 return false;\r
524                         if (pos == -1) \r
525                                 return false;\r
526                         if (value.endsWith("*"))\r
527                                 returnTodo = true;\r
528                         while (pos > -1) {\r
529                                 int endPos = n.getContent().indexOf("/>", pos);\r
530                                 String segment = n.getContent().substring(pos, endPos);\r
531                                 boolean currentState;\r
532                                 if (segment.toLowerCase().indexOf("checked=\"true\"") == -1)\r
533                                         currentState = false;\r
534                                 else\r
535                                         currentState = true;\r
536                                 if (desiredState == currentState)\r
537                                         returnTodo = true;\r
538                                 \r
539                                 pos = n.getContent().indexOf("<en-todo", pos+1);\r
540                         }\r
541                 }\r
542                 \r
543                 // Check resources\r
544                 for (int i=0; i<resource.size(); i++) {\r
545                         String resourceString = resource.get(i);\r
546                         resourceString = resourceString.replace("\"", "");\r
547                         negative = false;\r
548                         if (resourceString.startsWith("-"))\r
549                                 negative = true;\r
550                         resourceString = resourceString.substring(resourceString.indexOf(":")+1);\r
551                         if (resourceString.equals(""))\r
552                                 return false;\r
553                         for (int j=0; j<n.getResourcesSize(); j++) {\r
554                                 boolean match = stringMatch(n.getResources().get(j).getMime(), resourceString, negative);\r
555                                 if (!match)\r
556                                         return false;\r
557                                 returnResource = true;\r
558                         }\r
559                 }\r
560                 \r
561                 return returnResource && returnTodo && returnPhrase;\r
562         }\r
563         \r
564         private boolean stringMatch(String content, String text, boolean negative) {\r
565                 String regex;\r
566                 if (content == null && !negative)\r
567                         return false;\r
568                 if (content == null && negative)\r
569                         return true;\r
570                 \r
571                 if (text.endsWith("*")) {\r
572                         text = text.substring(0,text.length()-1);\r
573                         regex = text;\r
574                 } else {\r
575                         regex = text;\r
576                 }\r
577                 content = content.toLowerCase();\r
578                 regex = regex.toLowerCase();\r
579                 boolean matches = content.startsWith(regex);\r
580                 if (negative)\r
581                         return !matches;\r
582                 return matches;\r
583         }\r
584         \r
585         // Remove odd strings from search terms\r
586         private String cleanupWord(String word) {\r
587                 if (word.startsWith("\""))\r
588                         word = word.substring(1);\r
589                 if (word.endsWith("\""))\r
590             word = word.substring(0,word.length()-1);\r
591                 word = word.replace("\\\"","\"");\r
592                 word = word.replace("\\\\","\\");\r
593                 \r
594                 return word;\r
595         }\r
596 \r
597         \r
598         // Match dates\r
599         private boolean matchDatesAll(List<String> dates, long noteDate) {\r
600                 if (dates.size()== 0) \r
601                         return true;\r
602                 \r
603                 boolean negative = false;\r
604                 for (int i=0; i<dates.size(); i++) {\r
605                         String requiredDate = dates.get(i);\r
606                         if (requiredDate.startsWith("-"))\r
607                                 negative = true;\r
608                         \r
609                         int response = 0;\r
610                         requiredDate = requiredDate.substring(requiredDate.indexOf(":")+1);\r
611                         try {\r
612                                 response = dateCheck(requiredDate, noteDate);\r
613                         } catch (java.lang.NumberFormatException e) {return false;}  {\r
614                                 if (negative && response < 0)\r
615                                         return false;\r
616                                 if (!negative && response > 0)\r
617                                         return false;\r
618                         }\r
619                 }\r
620                 return true;\r
621         }\r
622         private boolean matchDatesAny(List<String> dates, long noteDate) {\r
623                 if (dates.size()== 0) \r
624                         return true;\r
625                 \r
626                 boolean negative = false;\r
627                 for (int i=0; i<dates.size(); i++) {\r
628                         String requiredDate = dates.get(i);\r
629                         if (requiredDate.startsWith("-"))\r
630                                 negative = true;\r
631                         \r
632                         int response = 0;\r
633                         requiredDate = requiredDate.substring(requiredDate.indexOf(":")+1);\r
634                         try {\r
635                                 response = dateCheck(requiredDate, noteDate);\r
636                         } catch (java.lang.NumberFormatException e) {return false;}  {\r
637                                 if (negative && response > 0)\r
638                                         return true;\r
639                                 if (!negative && response < 0)\r
640                                         return true;\r
641                         }\r
642                 }\r
643                 return false;\r
644         }\r
645         \r
646         @SuppressWarnings("unused")\r
647         private void printCalendar(Calendar calendar) {\r
648                 // define output format and print\r
649                 SimpleDateFormat sdf = new SimpleDateFormat("d MMM yyyy hh:mm:ss aaa");\r
650                 String date = sdf.format(calendar.getTime());\r
651                 System.err.print(date);\r
652                 calendar = new GregorianCalendar();\r
653         }\r
654         \r
655         \r
656         //****************************************\r
657         //****************************************\r
658         // Match search terms against notes\r
659         //****************************************\r
660         //****************************************\r
661         public List<Note> matchWords() {\r
662                 logger.log(logger.EXTREME, "Inside EnSearch.matchWords()");\r
663                 \r
664                 StringBuffer buffer = new StringBuffer(100);\r
665                 Integer counter = 0;\r
666                 boolean subSelect = false;\r
667                 \r
668                 buffer.append("Select guid from Note ");\r
669                 if (searchWords.size() > 0) \r
670                         subSelect = true;\r
671                 if (subSelect) {\r
672                         buffer.append(" where guid in ");\r
673                 \r
674                         // Build the query words\r
675                         String connector;\r
676                         if (any)\r
677                                 connector = new String("or");\r
678                         else\r
679                                 connector = new String("and");\r
680                         for (int i=0; i<getWords().size(); i++) {\r
681                                 buffer.append("(Select distinct guid from words where ");\r
682                                 buffer.append("weight >= :weight"+counter.toString() +" and ");\r
683                                 if (getWords().get(i).indexOf("*")==-1)\r
684                                         buffer.append("word=:word" +counter.toString());\r
685                                 else\r
686                                         buffer.append("word like :word" +counter.toString());\r
687                                 counter++;\r
688                                 buffer.append(") ");\r
689                                 if (i < getWords().size() -1)\r
690                                         buffer.append(" " +connector +" guid in ");\r
691                         }\r
692                 }\r
693                 \r
694                 NSqlQuery query = new NSqlQuery(conn.getConnection());\r
695                 \r
696                 if (!query.prepare(buffer.toString()))\r
697                         logger.log(logger.HIGH, "EnSearch Sql Prepare Failed:" +query.lastError());\r
698                 \r
699                 if (subSelect) {\r
700                         // Do the binding\r
701                         Integer binder = 0;\r
702                         for (int i=0; i<getWords().size(); i++) {\r
703                                 String val = getWords().get(i);\r
704                                 val = val.replace('*', '%');\r
705                                 query.bindValue(":weight"+binder.toString(), minimumRecognitionWeight);\r
706                                 query.bindValue(":word"+binder.toString(), cleanupWord(val));\r
707                                 binder++;\r
708                         }       \r
709                 }\r
710 \r
711                 List<Note> guids = new ArrayList<Note>();\r
712                 NoteTable noteTable = new NoteTable(logger, conn);  \r
713                 if (!query.exec()) \r
714                         logger.log(logger.EXTREME, "EnSearch.matchWords query failed: " +query.lastError());\r
715                 List<String> validGuids = new ArrayList<String>();\r
716                 while (query.next()) {\r
717                         String guid = query.valueString(0);\r
718                         validGuids.add(guid);\r
719                 }\r
720 \r
721                 List<Note> noteIndex = noteTable.getAllNotes();\r
722                 for (int i=0; i<noteIndex.size(); i++) {\r
723                         Note n = noteIndex.get(i);\r
724                         boolean good = true;\r
725                         \r
726                         if (!validGuids.contains(n.getGuid()))\r
727                                 good = false;\r
728                                                 \r
729                         // Start matching special stuff, like tags & notebooks\r
730                         if (any) {\r
731                                 if (good && !matchTagsAny(n.getTagNames()))\r
732                                         good = false;\r
733                                 if (good && !matchNotebook(n.getNotebookGuid()))\r
734                                         good = false;\r
735                                 if (good && !matchListAny(getIntitle(), n.getTitle()))\r
736                                         good = false;\r
737                                 if (good && !matchListAny(getAuthor(), n.getAttributes().getAuthor()))\r
738                                         good = false;\r
739                                 if (good && !matchListAny(getSource(), n.getAttributes().getSource()))\r
740                                         good = false;\r
741                                 if (good && !matchListAny(getSourceApplication(), n.getAttributes().getSourceApplication()))\r
742                                         good = false;\r
743                                 if (good && !matchContentAny(n))\r
744                                         good = false;\r
745                                 if (good && !matchDatesAny(getCreated(), n.getCreated()))\r
746                                         good = false;\r
747                                 if (good && !matchDatesAny(getUpdated(), n.getUpdated()))\r
748                                         good = false;\r
749                                 if (good && n.getAttributes() != null && !matchDatesAny(getSubjectDate(), n.getAttributes().getSubjectDate()))\r
750                                         good = false;\r
751                         } else {\r
752                                 if (good && !matchTagsAll(n.getTagNames()))\r
753                                         good = false;\r
754                                 if (good && !matchNotebook(n.getNotebookGuid()))\r
755                                         good = false;\r
756                                 if (good && !matchListAll(getIntitle(), n.getTitle()))\r
757                                         good = false;\r
758                                 if (good && !matchListAll(getAuthor(), n.getAttributes().getAuthor()))\r
759                                         good = false;\r
760                                 if (good && !matchListAll(getSource(), n.getAttributes().getSource()))\r
761                                         good = false;\r
762                                 if (good && !matchListAll(getSourceApplication(), n.getAttributes().getSourceApplication()))\r
763                                         good = false;\r
764                                 if (good && !matchContentAll(n))\r
765                                         good = false;\r
766                                 if (good && !matchDatesAll(getCreated(), n.getCreated()))\r
767                                         good = false;\r
768                                 if (good && !matchDatesAll(getUpdated(), n.getUpdated()))\r
769                                         good = false;\r
770                                 if (good && n.getAttributes() != null && !matchDatesAll(getSubjectDate(), n.getAttributes().getSubjectDate()))\r
771                                         good = false;\r
772                         }\r
773                         if (good) {\r
774                                 guids.add(n);\r
775                         }\r
776                 }\r
777                 \r
778                 // For performance reasons, we didn't get the tags for every note individually.  We now need to \r
779                 // get them\r
780                 List<NoteTagsRecord> noteTags = noteTable.noteTagsTable.getAllNoteTags();\r
781                 for (int i=0; i<guids.size(); i++) {\r
782                         List<String> tags = new ArrayList<String>();\r
783                         List<String> names = new ArrayList<String>();\r
784                         for (int j=0; j<noteTags.size(); j++) {\r
785                                 if (guids.get(i).getGuid().equals(noteTags.get(j).noteGuid)) {\r
786                                         tags.add(noteTags.get(j).tagGuid);\r
787                                         names.add(getTagNameByGuid(noteTags.get(j).tagGuid));\r
788                                 }\r
789                         }\r
790                         \r
791                         guids.get(i).setTagGuids(tags);\r
792                         guids.get(i).setTagNames(names);\r
793                 };\r
794                 logger.log(logger.EXTREME, "Leaving EnSearch.matchWords()");\r
795                 return guids;\r
796         }\r
797         \r
798         \r
799         \r
800         private String getTagNameByGuid(String guid) {\r
801                 for (int i=0; i<tagIndex.size(); i++) {\r
802                         if (tagIndex.get(i).getGuid().equals(guid)) \r
803                                         return tagIndex.get(i).getName();\r
804                 }               \r
805                 return "";\r
806         }\r
807 \r
808         // Compare dates\r
809         public int dateCheck(String date, long noteDate)  throws java.lang.NumberFormatException  {\r
810                 int offset = 0;\r
811                 boolean found = false;\r
812                 GregorianCalendar calendar = new GregorianCalendar();\r
813                 \r
814                 if (date.contains("-")) {\r
815                         String modifier = date.substring(date.indexOf("-")+1);\r
816                         offset = new Integer(modifier);\r
817                         offset = 0-offset;\r
818                         date = date.substring(0,date.indexOf("-"));\r
819                 }\r
820                 \r
821                 if (date.contains("+")) {\r
822                         String modifier = date.substring(date.indexOf("+")+1);\r
823                         offset = new Integer(modifier);\r
824                         date = date.substring(0,date.indexOf("+"));\r
825                 }\r
826                 \r
827                 if (date.equalsIgnoreCase("today")) {\r
828                         calendar.add(Calendar.DATE, offset);\r
829                         calendar.set(Calendar.HOUR, 0);\r
830                         calendar.set(Calendar.MINUTE, 0);\r
831                         calendar.set(Calendar.SECOND, 1);\r
832                         found = true;\r
833                 }\r
834                 \r
835                 if (date.equalsIgnoreCase("month")) {\r
836                         calendar.add(Calendar.MONTH, offset);\r
837                         calendar.set(Calendar.DAY_OF_MONTH, 1);\r
838                         calendar.set(Calendar.HOUR, 0);\r
839                         calendar.set(Calendar.MINUTE, 0);\r
840                         calendar.set(Calendar.SECOND, 1);\r
841                         found = true;\r
842                 }\r
843 \r
844                 if (date.equalsIgnoreCase("year")) {\r
845                         calendar.add(Calendar.YEAR, offset);\r
846                         calendar.set(Calendar.MONTH, Calendar.JANUARY);\r
847                         calendar.set(Calendar.DAY_OF_MONTH, 1);\r
848                         calendar.set(Calendar.HOUR, 0);\r
849                         calendar.set(Calendar.MINUTE, 0);\r
850                         calendar.set(Calendar.SECOND, 1);\r
851                         found = true;\r
852                 }\r
853 \r
854                 if (date.equalsIgnoreCase("week")) {\r
855                         calendar.add(Calendar.DATE, 0-calendar.get(Calendar.DAY_OF_WEEK)+1);\r
856                         calendar.add(Calendar.DATE,(offset*7));\r
857                         calendar.set(Calendar.HOUR, 0);\r
858                         calendar.set(Calendar.MINUTE, 0);\r
859                         calendar.set(Calendar.SECOND, 1);\r
860 \r
861                         found = true;\r
862                 }\r
863                 \r
864                 // If nothing was found, then we have a date number\r
865                 if (!found) {\r
866                         calendar = stringToGregorianCalendar(date);\r
867                 }\r
868                 \r
869                 \r
870                 String dateTimeFormat = new String("yyyyMMdd-HHmmss");\r
871                 SimpleDateFormat simple = new SimpleDateFormat(dateTimeFormat);\r
872                 StringBuilder creationDate = new StringBuilder(simple.format(noteDate));\r
873                 GregorianCalendar nCalendar = stringToGregorianCalendar(creationDate.toString().replace("-", "T"));\r
874                 if (calendar == null || nCalendar == null)  // If we have something invalid, it automatically fails\r
875                         return 1;\r
876                 return calendar.compareTo(nCalendar);\r
877         }\r
878         private GregorianCalendar stringToGregorianCalendar(String date) {\r
879                 String datePart = date;\r
880                 GregorianCalendar calendar = new GregorianCalendar();\r
881                 boolean GMT = false;\r
882                 String timePart = "";\r
883                 if (date.contains("T")) {\r
884                         datePart = date.substring(0,date.indexOf("T"));\r
885                         timePart = date.substring(date.indexOf("T")+1);\r
886                 } else {\r
887                         timePart = "000001";\r
888                 }\r
889                 if (datePart.length() != 8)\r
890                         return null;\r
891                 calendar.set(Calendar.YEAR, new Integer(datePart.substring(0,4)));\r
892                 calendar.set(Calendar.MONTH, new Integer(datePart.substring(4,6))-1);\r
893                 calendar.set(Calendar.DAY_OF_MONTH, new Integer(datePart.substring(6)));\r
894                 if (timePart.endsWith("Z")) {\r
895                         GMT = true;\r
896                         timePart = timePart.substring(0,timePart.length()-1);\r
897                 }\r
898                 timePart = timePart.concat("000000");\r
899                 timePart = timePart.substring(0,6);\r
900                 calendar.set(Calendar.HOUR, new Integer(timePart.substring(0,2)));\r
901                 calendar.set(Calendar.MINUTE, new Integer(timePart.substring(2,4)));\r
902                 calendar.set(Calendar.SECOND, new Integer(timePart.substring(4)));\r
903                 if (GMT)\r
904                         calendar.set(Calendar.ZONE_OFFSET, -1*(calendar.get(Calendar.ZONE_OFFSET)/(1000*60*60)));\r
905                 return calendar;\r
906 \r
907         }\r
908                 \r
909 }\r