OSDN Git Service

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