OSDN Git Service

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