1 /* Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2  * Copyright (C) 2009 - DIGITEO - Bernard HUGUENEY
3  * Copyright (C) 2010 - Calixte DENIZET
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15 
16 package org.scilab.modules.scinotes;
17 
18 import java.io.BufferedReader;
19 import java.io.BufferedWriter;
20 import java.io.File;
21 import java.io.FilenameFilter;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.io.FileInputStream;
25 import java.nio.charset.Charset;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 import java.util.Scanner;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 
33 import javax.swing.Icon;
34 import javax.swing.ImageIcon;
35 import javax.swing.SwingWorker;
36 import javax.swing.text.BadLocationException;
37 import javax.swing.text.Document;
38 import javax.swing.tree.DefaultMutableTreeNode;
39 
40 import org.scilab.modules.commons.gui.FindIconHelper;
41 import org.scilab.modules.commons.xml.ScilabXMLUtilities;
42 import org.scilab.modules.scinotes.utils.SciNotesMessages;
43 import org.w3c.dom.Element;
44 import org.w3c.dom.NodeList;
45 
46 /**
47  * Class SearchManager
48  * @author Sylvestre Koumar
49  * @author Calixte DENIZET
50  */
51 public class SearchManager {
52 
53     private static final ImageIcon FILEIMAGE = new ImageIcon(FindIconHelper.findIcon("stock_search"));
54     private static final ImageIcon SCILABFILEIMAGE = new ImageIcon(FindIconHelper.findIcon("scilab_search"));
55     private static final ImageIcon FOLDERIMAGE = new ImageIcon(FindIconHelper.findIcon("folder-saved-search"));
56     private static final ImageIcon LINEICON = new ImageIcon(FindIconHelper.findIcon("line-found"));
57 
58     /**
59      * FIND AND REPLACE START
60      * @param scilabDocument document
61      * @param word string
62      * @param start int
63      * @param end int
64      * @param caseSensitive boolean
65      * @param wholeWord boolean
66      * @param useRegexp boolean
67      * @return List
68      */
findWord(Document scilabDocument, String word, int start, int end, boolean caseSensitive, boolean wholeWord, boolean useRegexp)69     public static List<Integer[]> findWord(Document scilabDocument, String word,
70                                            int start, int end,
71                                            boolean caseSensitive, boolean wholeWord, boolean useRegexp) {
72         String fullText = "";
73         try {
74             fullText = scilabDocument.getText(start, end - start + 1);
75         } catch (BadLocationException ex) {
76             ex.printStackTrace();
77         }
78 
79         if (fullText.length() == 0) {
80             return null;
81         }
82 
83         List<Integer[]> offsetList = new ArrayList<Integer[]>();
84 
85         //If we don't give any word to find
86         if (word != null && !word.equals("")) {
87             Pattern pattern = generatePattern(word, caseSensitive, wholeWord, useRegexp);
88             Matcher matcher = pattern.matcher(fullText);
89 
90             while (matcher.find()) {
91                 if (matcher.start() != matcher.end()) {
92                     offsetList.add(new Integer[] {matcher.start() + start, matcher.end() + start});
93                 }
94             }
95         }
96 
97         return offsetList;
98     }
99 
100     /**
101      * FIND AND REPLACE START
102      * @param scilabDocument document
103      * @param word string
104      * @param start int
105      * @param end int
106      * @param caseSensitive boolean
107      * @param wholeWord boolean
108      * @param useRegexp boolean
109      * @return List
110      */
findToken(ScilabDocument scilabDocument, int token, ScilabLexer lexer, Pattern pat)111     public static List<Integer[]> findToken(ScilabDocument scilabDocument, int token, ScilabLexer lexer, Pattern pat) {
112         String fullText = scilabDocument.getText();
113 
114         if (fullText.length() == 0) {
115             return null;
116         }
117 
118         List<Integer[]> offsetList = new ArrayList<Integer[]>();
119 
120         //If we don't give any word to find
121         Matcher matcher = pat.matcher(fullText);
122 
123         while (matcher.find()) {
124             int start = matcher.start();
125             int end = matcher.end();
126             if (token == -1 || token == lexer.getKeyword(start, false)) {
127                 offsetList.add(new Integer[] {start, end});
128             }
129         }
130 
131         return offsetList;
132     }
133 
134     /**
135      * Generate the good pattern according to the differents boolean
136      * @param exp the searched expression
137      * @param caseSensitive boolean
138      * @param wholeWord boolean
139      * @param useRegexp boolean
140      * @return the pattern
141      */
generatePattern(String exp, boolean caseSensitive, boolean wholeWord, boolean useRegexp)142     public static Pattern generatePattern(String exp, boolean caseSensitive, boolean wholeWord, boolean useRegexp) {
143         String word = exp;
144         if (word != null && !word.equals("")) {
145             if (!useRegexp) {
146                 word = word.replace("\\E", "\\E\\\\E\\Q");
147                 word = "\\Q" + word + "\\E";
148                 if (wholeWord && exp.matches("\\b.*\\b")) {
149                     word = "\\b" + word + "\\b";
150                 }
151             }
152 
153             if (!caseSensitive) {
154                 word = "(?i)" + word;
155             }
156 
157             if (useRegexp) {
158                 word = "(?m)" + word;
159             }
160 
161             return Pattern.compile(word);
162         } else {
163             return Pattern.compile("");
164         }
165     }
166 
167     /**
168      * Search a word (with a pattern) in files selected according to their name.
169      * @param base the base directory
170      * @param recursive, if true then a recursive search is made
171      * @param ignoreCR, if true then the read file is considered as one line and regex pattern can include \n
172      * @param filePattern the pattern to use to select the files. * is equivalent to .* and ? to .?
173      * @param fileCaseSensitive, if true then the file pattern is case sensitive
174      * @param wordPattern the pattern of the word to search
175      * @param wordCaseSensitive, if true then the word pattern is case sensitive
176      * @param wholeWord, if true only whole word will be matched, e.g. in "foobar foo bar", if the pattern is "foo", then only the second "foo" will be matched
177      * @param regexp, if true the word pattern is considered as a regex
178      * @return infos with the matching positions
179      */
searchInFiles(final BackgroundSearch bgs, String base, final boolean recursive, final boolean ignoreCR, String filePattern, boolean fileCaseSensitive, String wordPattern, boolean wordCaseSensitive, boolean wholeWord, boolean regexp)180     public static MatchingPositions searchInFiles(final BackgroundSearch bgs, String base, final boolean recursive, final boolean ignoreCR,
181             String filePattern, boolean fileCaseSensitive,
182             String wordPattern, boolean wordCaseSensitive, boolean wholeWord, boolean regexp) {
183         final File dir = new File(base);
184         Pattern word = null;
185         if (wordPattern != null && wordPattern.length() != 0) {
186             word = generatePattern(wordPattern, wordCaseSensitive, wholeWord, regexp);
187         }
188         filePattern = filePattern.replace(".", "\\.").replace("*", ".*").replace("?", ".?");
189         final Pattern file = generatePattern(filePattern, fileCaseSensitive, false, true);
190 
191         final boolean[] killed = new boolean[] {false};
192         if (bgs == null) {
193             return searchInFiles(killed, dir, recursive, ignoreCR, file, word);
194         } else {
195             final Pattern fword = word;
196             SwingWorker worker = new SwingWorker<Object, Object>() {
197                 @Override
198                 public Object doInBackground() {
199                     long begin = System.currentTimeMillis();
200                     bgs.setResults(searchInFiles(killed, dir, recursive, ignoreCR, file, fword));
201                     long end = System.currentTimeMillis();
202                     bgs.setElapsedTime(end - begin);
203                     return null;
204                 }
205 
206                 @Override
207                 public void done() {
208                     bgs.done();
209                 }
210             };
211             bgs.setKilled(killed);
212             bgs.setSwingWorker(worker);
213             worker.execute();
214             return null;
215         }
216     }
217 
218     /**
219      * Search a word (with a pattern) in files selected according to their name.
220      * @param base the base directory
221      * @param recursive, if true then a recursive search is made
222      * @param ignoreCR, if true then the read file is considered as one line and regex pattern can include \n
223      * @param filePattern the pattern to use to select the files. * is equivalent to .* and ? to .?
224      * @param word the pattern of the word to search
225      * @param killed a boolean array with more than one element. It is used as a reference on a boolean set to true if the process is killed.
226      * @return infos with the matching positions
227      */
searchInFiles(boolean[] killed, File base, boolean recursive, boolean ignoreCR, final Pattern file, final Pattern word)228     public static MatchingPositions searchInFiles(boolean[] killed, File base, boolean recursive, boolean ignoreCR, final Pattern file, final Pattern word) {
229         MatchingPositions pos = null;
230         if (base.exists() && base.isDirectory() && base.canRead() && !killed[0]) {
231             List<MatchingPositions> list = new ArrayList<MatchingPositions>();
232             pos = new MatchingPositions(base.getAbsolutePath(), list);
233             int occurences = 0;
234             File[] files = base.listFiles(new FilenameFilter() {
235                 @Override
236                 public boolean accept(File dir, String name) {
237                     File f = new File(dir, name);
238                     return f.isFile() && f.canRead() && file.matcher(name).matches();
239                 }
240             });
241             Arrays.sort(files);
242 
243             if (word != null) {
244                 for (int i = 0; i < files.length && !killed[0]; i++) {
245                     MatchingPositions wpos;
246                     if (!ignoreCR) {
247                         wpos = searchWordInFile(files[i], word);
248                     } else {
249                         wpos = searchWordInFileIgnoringCR(files[i], word);
250                     }
251                     if (wpos != null && wpos.getOccurences() != 0) {
252                         list.add(wpos);
253                         occurences += wpos.getOccurences();
254                     }
255                 }
256             } else {
257                 for (int i = 0; i < files.length && !killed[0]; i++) {
258                     list.add(new MatchingPositions(files[i].getAbsolutePath()));
259                 }
260                 occurences += files.length;
261             }
262 
263             if (recursive) {
264                 files = base.listFiles(new FilenameFilter() {
265                     @Override
266                     public boolean accept(File dir, String name) {
267                         File d = new File(dir, name);
268                         return d.isDirectory() && d.canRead();
269                     }
270                 });
271                 Arrays.sort(files);
272 
273                 for (int i = 0; i < files.length && !killed[0]; i++) {
274                     MatchingPositions rpos = searchInFiles(killed, files[i], true, ignoreCR, file, word);
275                     if (rpos != null) {
276                         list.add(rpos);
277                         occurences += rpos.getOccurences();
278                     }
279                 }
280             }
281 
282             pos.setOccurences(occurences);
283 
284             if (list.isEmpty()) {
285                 return null;
286             }
287         }
288 
289         return pos;
290     }
291 
292     /**
293      * Search a word (with a pattern) in a file. The search is made line by line.
294      * @param file the file where to search
295      * @param pat the pattern of the word to search
296      * @return infos with the matching positions
297      */
searchWordInFile(File f, Pattern pat)298     public static MatchingPositions searchWordInFile(File f, Pattern pat) {
299         if (f.exists() && f.canRead()) {
300             MatchingPositions pos = new MatchingPositions(f.getAbsolutePath());
301             String charset;
302             try {
303                 charset = ScilabEditorKit.tryToGuessEncoding(f).name();
304             } catch (Exception e) {
305                 charset = Charset.defaultCharset().name();
306             }
307 
308             try {
309                 Scanner scanner = new Scanner(f, charset);
310                 int occ = 0;
311                 int line = 0;
312                 while (scanner.hasNextLine()) {
313                     line++;
314                     String str = scanner.nextLine();
315                     Matcher matcher = pat.matcher(str);
316                     int socc = occ;
317                     while (matcher.find()) {
318                         occ++;
319                     }
320                     if (occ != socc) {
321                         pos.addLine(line, str, pat);
322                     }
323                 }
324                 scanner.close();
325 
326                 pos.setOccurences(occ);
327                 return pos;
328             } catch (Exception e) { }
329         }
330 
331         return null;
332     }
333 
334     /**
335      * Search a word (with a pattern) in a file. The file content is considered as one line (useful to search "...\n...")
336      * @param file the file where to search
337      * @param pat the pattern of the word to search
338      * @return infos with the matching positions
339      */
searchWordInFileIgnoringCR(File f, Pattern pat)340     public static MatchingPositions searchWordInFileIgnoringCR(File f, Pattern pat) {
341         if (f.exists() && f.canRead()) {
342             MatchingPositions pos = new MatchingPositions(f.getAbsolutePath());
343             String charset;
344             try {
345                 charset = ScilabEditorKit.tryToGuessEncoding(f).name();
346             } catch (Exception e) {
347                 charset = Charset.defaultCharset().name();
348             }
349 
350             try {
351                 Scanner scanner = new Scanner(f, charset);
352                 int occ = 0;
353                 while (scanner.findWithinHorizon(pat, 0) != null) {
354                     occ++;
355                 }
356                 pos.setOccurences(occ);
357                 scanner.close();
358 
359                 return pos;
360             } catch (Exception e) { }
361         }
362 
363         return null;
364     }
365 
366     /**
367      * Count the file having a name corresponding to a pattern
368      * @param base the base directory
369      * @param pat the file name pattern
370      * @return the number of files
371      */
countFiles(File base, Pattern pat)372     public static int countFiles(File base, Pattern pat) {
373         if (!base.isDirectory() || !base.canRead()) {
374             return -1;
375         }
376 
377         int[] count = new int[] {0};
378         countFiles(base, pat, count);
379 
380         return count[0];
381     }
382 
383     /**
384      * Count files in a recursive way
385      */
countFiles(File base, final Pattern pat, final int[] count)386     private static void countFiles(File base, final Pattern pat, final int[] count) {
387         File[] files = base.listFiles(new FilenameFilter() {
388             @Override
389             public boolean accept(File dir, String name) {
390                 File f = new File(dir, name);
391                 if (f.isFile() && f.canRead() && pat.matcher(name).matches()) {
392                     count[0]++;
393                 } else if (f.isDirectory() && f.canRead()) {
394                     countFiles(f, pat, count);
395                 }
396                 return false;
397             }
398         });
399     }
400 
401     /**
402      * @param file the file to test
403      * @return true if it is a binary file
404      */
isBinaryFile(File f)405     public static boolean isBinaryFile(File f) {
406         try ( BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8")) ) {
407             char[] buffer = new char[8192];
408             int len = reader.read(buffer, 0, 8192);
409 
410             int i = 0;
411             if (len != -1) {
412                 for (; i < len && buffer[i] != '\0'; i++) {
413                     ;
414                 }
415             }
416 
417             return len != -1 && i != len;
418         } catch (IOException e) {
419             e.printStackTrace();
420             return false;
421         }
422     }
423 
indent(BufferedWriter buffer, int level)424     private static void indent(BufferedWriter buffer, int level) throws IOException {
425         for (int i = 0; i < level; i++) {
426             buffer.append("    ");
427         }
428     }
429 
430     /**
431      * MatchingPositions: inner class to store the results of a search in a file or in a directory
432      */
433     public static class MatchingPositions implements Iconable {
434 
435         private final String file;
436         private boolean isRoot;
437         private Icon icon;
438         private int occurences;
439         private List<MatchingPositions> children;
440         private final List<Line> lines = new ArrayList<Line>();
441 
442         /**
443          * Constructor
444          * @param file the file where to search is made
445          * @param children the list of the file in the directory
446          */
MatchingPositions(String file, List<MatchingPositions> children)447         public MatchingPositions(String file, List<MatchingPositions> children) {
448             this.file = file;
449             this.children = children;
450             setIcon();
451         }
452 
453         /**
454          * Constructor
455          * @param file the file where to search is made
456          */
MatchingPositions(String file)457         public MatchingPositions(String file) {
458             this(file, null);
459         }
460 
461         /**
462          * @param root the xml Element representing a MatchingPositions
463          */
MatchingPositions(Element root)464         public MatchingPositions(Element root) {
465             this.file = root.getAttribute("file");
466             this.isRoot = Boolean.parseBoolean(root.getAttribute("isRoot"));
467             this.occurences = Integer.parseInt(root.getAttribute("occurences"));
468             if (root.hasChildNodes()) {
469                 NodeList nodeList = root.getChildNodes();
470                 Element e = (Element) nodeList.item(1);
471                 if (e.getTagName().equals("Position")) {
472                     this.children = new ArrayList<MatchingPositions>();
473                     for (int i = 0; i < nodeList.getLength(); i++) {
474                         if (nodeList.item(i) instanceof Element) {
475                             e = (Element) nodeList.item(i);
476                             this.children.add(new MatchingPositions(e));
477                         }
478                     }
479                 } else {
480                     for (int i = 0; i < nodeList.getLength(); i++) {
481                         if (nodeList.item(i) instanceof Element) {
482                             e = (Element) nodeList.item(i);
483                             this.lines.add(new Line(e));
484                         }
485                     }
486                 }
487             }
488             setIcon();
489         }
490 
setIcon()491         private void setIcon() {
492             if (children != null) {
493                 this.icon = FOLDERIMAGE;
494             } else if (file.endsWith(".sce") || file.endsWith(".sci")) {
495                 this.icon = SCILABFILEIMAGE;
496             } else {
497                 this.icon = FILEIMAGE;
498             }
499         }
500 
501         /**
502          * Set this as the root directory
503          */
setRoot()504         public void setRoot() {
505             isRoot = true;
506         }
507 
508         /**
509          * Set the number of matches in the file or in the directory
510          * @param occ the number of matches
511          */
setOccurences(int occ)512         public void setOccurences(int occ) {
513             occurences = occ;
514         }
515 
516         /**
517          * @return the number of matches
518          */
getOccurences()519         public int getOccurences() {
520             return occurences;
521         }
522 
523         /**
524          * Add a line where the searched word has been found
525          * @param number the line number
526          * @param line the line content
527          * @param pat the pattern used
528          */
addLine(int number, String line, Pattern pat)529         public void addLine(int number, String line, Pattern pat) {
530             this.lines.add(new Line(number, line, pat));
531         }
532 
533         /**
534          * @return the file name
535          */
getFileName()536         public String getFileName() {
537             return file;
538         }
539 
540         /**
541          * @return true if we are in a directory
542          */
isDirectory()543         public boolean isDirectory() {
544             return children != null;
545         }
546 
547         /**
548          * {@inheritDoc}
549          */
550         @Override
getIcon()551         public Icon getIcon() {
552             return icon;
553         }
554 
555         /**
556          * @return true if the file have matching lines
557          */
hasLines()558         public boolean hasLines() {
559             return !lines.isEmpty();
560         }
561 
562         /**
563          * @return the file present in this directory
564          */
getChildren()565         public List<MatchingPositions> getChildren() {
566             return children;
567         }
568 
569         /**
570          * Convert this MatchingPositions and its children (if they are) in a DefaultMutableTreeNode
571          * @return the coirresponding DefaultMutableTreeNode
572          */
toDefaultMutableTreeNode()573         public DefaultMutableTreeNode toDefaultMutableTreeNode() {
574             DefaultMutableTreeNode root = new DefaultMutableTreeNode(this);
575             if (children != null && !children.isEmpty()) {
576                 for (int i = 0; i < children.size(); i++) {
577                     root.add(children.get(i).toDefaultMutableTreeNode());
578                 }
579             } else if (!lines.isEmpty()) {
580                 for (Line l : lines) {
581                     root.add(new DefaultMutableTreeNode(l));
582                 }
583             }
584 
585             return root;
586         }
587 
588         /**
589          * @param buffer the buffer where to write the XML
590          * @param level the indentation level
591          */
toXML(BufferedWriter buffer, int level)592         public void toXML(BufferedWriter buffer, int level) throws IOException {
593             indent(buffer, level);
594             buffer.append("<Position file=\"" + ScilabXMLUtilities.getXMLString(file) + "\" isRoot=\"" + isRoot + "\" occurences=\"" + occurences + "\"");
595             if (children != null && !children.isEmpty()) {
596                 buffer.append(">\n");
597                 for (int i = 0; i < children.size(); i++) {
598                     children.get(i).toXML(buffer, level + 1);
599                 }
600                 indent(buffer, level);
601                 buffer.append("</Position>\n");
602             } else if (!lines.isEmpty()) {
603                 buffer.append(">\n");
604                 for (Line l : lines) {
605                     l.toXML(buffer, level + 1);
606                 }
607                 indent(buffer, level);
608                 buffer.append("</Position>\n");
609             } else {
610                 buffer.append("/>\n");
611             }
612         }
613 
614         /**
615          * {@inheritDoc}
616          */
617         @Override
toString()618         public String toString() {
619             String occ = SciNotesMessages.MATCHES;
620             if (occurences <= 1) {
621                 occ = SciNotesMessages.MATCH;
622             }
623 
624             String filename;
625             if ((!isDirectory() && occurences != 0)) {
626                 filename = new File(getFileName()).getName();
627             } else if (isRoot) {
628                 filename = getFileName();
629             } else {
630                 filename = new File(getFileName()).getName();
631             }
632 
633             if (occurences == 0 && !isRoot) {
634                 return filename;
635             }
636 
637             filename = filename.replace("&", "&amp;").replace("/", "&#47;").replace("\\", "&#92;").replace("<", "&lt;").replace(">", "&gt;");
638             return String.format(occ, filename, occurences);
639         }
640     }
641 
642     /**
643      * Line: inner class to store a line number and line content
644      */
645     public static class Line implements Iconable {
646 
647         private final int number;
648         private String content;
649 
650         /**
651          * Constructor
652          * @param number the line number
653          * @param content the line content
654          * @param pattern the used pattern
655          */
Line(int number, String content, Pattern pattern)656         public Line(int number, String content, Pattern pattern) {
657             this.number = number;
658             Matcher matcher = pattern.matcher(content);
659             if (content.length() > 128) {
660                 content = content.substring(0, 128) + "...";
661             }
662             StringBuffer sb = new StringBuffer();
663             while (matcher.find()) {
664                 /*
665                   TODO: Find a better way to handle <b> and </b> around the pattern.
666                   If HTML entities are put before the loop the pattern should be updated (not a funky task...).
667                   Actually, it could have a bad rendering on binary files (which probably contains \0...)
668                 */
669                 matcher.appendReplacement(sb, "\0");
670                 sb.append(matcher.group());
671                 sb.append("\0\0");
672             }
673             matcher.appendTail(sb);
674             this.content = sb.toString();
675             this.content = this.content.replace("&", "&amp;").replace("/", "&#47;").replace("\\", "&#92;").replace("<", "&lt;").replace(">", "&gt;").replace("\0\0", "</b>").replace("\0", "<b>");
676         }
677 
678         /**
679          * @param root the xml Element representing a Line
680          */
Line(Element root)681         public Line(Element root) {
682             this.number = Integer.parseInt(root.getAttribute("number"));
683             this.content = root.getAttribute("content");
684         }
685 
686         /**
687          * @return the line number
688          */
getNumber()689         public int getNumber() {
690             return number;
691         }
692 
693         /**
694          * @return the line content as HTML
695          */
getContent()696         public String getContent() {
697             return content;
698         }
699 
700         /**
701          * {@inheritDoc}
702          */
703         @Override
getIcon()704         public Icon getIcon() {
705             return LINEICON;
706         }
707 
708         /**
709          * @param buffer the buffer where to write the XML
710          * @param level the indentation level
711          */
toXML(BufferedWriter buffer, int level)712         public void toXML(BufferedWriter buffer, int level) throws IOException {
713             indent(buffer, level);
714             buffer.append("<Line content=\"" + ScilabXMLUtilities.getXMLString(content) + "\" number=\"" + number + "\"/>\n");
715         }
716 
717         /**
718          * {@inheritDoc}
719          */
720         @Override
toString()721         public String toString() {
722             return "<html><u>line " + number + "</u>&thinsp;: " + content + "</html>";
723         }
724     }
725 
726     /**
727      * Inner interface for the JTree representation
728      */
729     public static interface Iconable {
730 
731         /**
732          * @return the icon used in the JTree representation
733          */
getIcon()734         public Icon getIcon();
735     }
736 
737     /**
738      * Inner class to allow a background search
739      */
740     public static abstract class BackgroundSearch {
741 
742         private MatchingPositions pos;
743         private SwingWorker worker;
744         private long time;
745         private boolean[] killed;
746 
747         /**
748          * Default Constructor
749          */
BackgroundSearch()750         public BackgroundSearch() { }
751 
752         /**
753          * Stop this search
754          */
stop()755         public void stop() {
756             if (worker != null && !isDone()) {
757                 worker.cancel(true);
758                 worker = null;
759                 if (killed != null && killed.length >= 1) {
760                     killed[0] = true;
761                 }
762             }
763         }
764 
765         /**
766          * Called when the work is done
767          */
done()768         public abstract void done();
769 
770         /**
771          * @return true if the search is finished
772          */
isDone()773         public boolean isDone() {
774             if (worker != null) {
775                 return worker.isDone();
776             }
777             return true;
778         }
779 
780         /**
781          * Get the results
782          * @return the results
783          */
getResults()784         public MatchingPositions getResults() {
785             if (isDone()) {
786                 worker = null;
787                 return pos;
788             }
789             return null;
790         }
791 
792         /**
793          * Set the elapsed time for this search
794          */
setElapsedTime(long t)795         public void setElapsedTime(long t) {
796             this.time = t;
797         }
798 
799         /**
800          * @return the elapsed time of this search
801          */
getElapsedTime()802         public long getElapsedTime() {
803             return time;
804         }
805 
806         /**
807          * Set the SwingWorker we work with
808          * @param worker the SwingWorker
809          */
setSwingWorker(SwingWorker worker)810         private void setSwingWorker(SwingWorker worker) {
811             this.worker = worker;
812         }
813 
814         /**
815          * @param killed a reference on a boolean to inform the main loop that the process has been killed
816          */
setKilled(boolean[] killed)817         private void setKilled(boolean[] killed) {
818             this.killed = killed;
819         }
820 
821         /**
822          * @param pos the results to set
823          */
setResults(MatchingPositions pos)824         private void setResults(MatchingPositions pos) {
825             this.pos = pos;
826         }
827     }
828 }
829