OSDN Git Service

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