]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.common/src/org/simantics/browsing/ui/common/views/DefaultFilterStrategy.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.browsing.ui.common / src / org / simantics / browsing / ui / common / views / DefaultFilterStrategy.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.browsing.ui.common.views;\r
13 \r
14 import java.nio.CharBuffer;\r
15 import java.util.regex.Matcher;\r
16 import java.util.regex.Pattern;\r
17 \r
18 /**\r
19  * Default implementation of IFilterStrategy.\r
20  * \r
21  * <p>\r
22  * It implements simple search semantics with only the special wildcard\r
23  * characters '*' ( 0 to n any characters) and '?' (any one character)\r
24  * recognized. In order to allow the filter to pass arbitrary prefixes, the\r
25  * client has to give a '*' prefix in the filter string. On the contrary, the\r
26  * client does not have to specify a '*' in order to pass arbitrary suffixes -\r
27  * arbitrary suffixes are allowed by default by this strategy.\r
28  * \r
29  * <p>\r
30  * This strategy forces the filter string to lowercase.\r
31  * \r
32  * TODO: implement case-insensitiveness properly, not by forcing the search string into lower case. Also Fix FilterSelectionRequestQueryProcessor after doing this.\r
33  * \r
34  * @author Tuukka Lehtonen\r
35  */\r
36 public class DefaultFilterStrategy implements IFilterStrategy {\r
37 \r
38     private static final boolean DEBUG = false;\r
39 \r
40     boolean implicitPreAsterisk = true;\r
41 \r
42     public DefaultFilterStrategy() {\r
43         this(true);\r
44     }\r
45 \r
46     public DefaultFilterStrategy(boolean implicitPreAsterisk) {\r
47         this.implicitPreAsterisk = implicitPreAsterisk;\r
48     }\r
49 \r
50     private static StringBuilder addSearchWord(StringBuilder sb, String pattern) {\r
51         if (DEBUG)\r
52             System.out.println("addSearchWord(" + pattern + ") to '" + sb.toString() + "'");\r
53 \r
54         if (pattern == null || pattern.isEmpty())\r
55             return sb;\r
56         if (sb.length() > 0)\r
57             sb.append('|');\r
58         sb.append('(');\r
59         sb.append(pattern);\r
60         sb.append(')');\r
61         return sb;\r
62     }\r
63 \r
64     private static String toString(CharBuffer cb) {\r
65         cb.limit(cb.position());\r
66         cb.reset();\r
67         if (DEBUG)\r
68             System.out.println("toString(" + cb + ")");\r
69         String result = cb.toString();\r
70         cb.limit(cb.capacity());\r
71         return result;\r
72     }\r
73 \r
74     public static String toSinglePatternString(String filter, boolean implicitPreAsterisk) {\r
75         if (!filter.isEmpty()) {\r
76             // Force searching in lowercase.\r
77             filter = filter.toLowerCase();\r
78 \r
79             // Construct a regular expression from the specified text.\r
80             String regExFilter = filter\r
81             .replace("\\", "\\\\")   // \ -> \\\r
82             .replace(".", "\\.")     // . -> \.\r
83             .replace("*", ".*")      // * -> Any 0..n characters\r
84             .replace("?", ".")       // ? -> Any single character\r
85             .replace("+", "\\+")     // + -> \+\r
86             .replace("(", "\\(")     // ( -> \(\r
87             .replace(")", "\\)")     // ) -> \)\r
88             .replace("[", "\\[")     // [ -> \[\r
89             .replace("]", "\\]")     // ] -> \]\r
90             .replace("{", "\\{")     // { -> \{\r
91             .replace("}", "\\}")     // } -> \}\r
92             .replace("^", "\\^")     // ^ -> \^\r
93             .replace("$", "\\$")     // $ -> \$\r
94             .replace("|", ".*|")     // $ -> \$\r
95             //.replace("|", "\\|")     // | -> \|\r
96             .replace("&&", "\\&&")   // && -> \&&\r
97             ;\r
98 \r
99             if (implicitPreAsterisk)\r
100                 if (!regExFilter.startsWith(".*"))\r
101                     regExFilter = ".*" + regExFilter ;\r
102             if (!regExFilter.endsWith(".*"))\r
103                 regExFilter += ".*" ;\r
104 \r
105             return regExFilter;\r
106         }\r
107         return null;\r
108     }\r
109 \r
110     public static String defaultToPatternString(String filter, boolean implicitPreAsterisk) {\r
111         if (filter.isEmpty())\r
112             return null;\r
113 \r
114         CharBuffer buf = CharBuffer.allocate(filter.length()*2);\r
115         buf.mark();\r
116         StringBuilder sb = new StringBuilder(filter.length()*2);\r
117         boolean inQuote = false;\r
118         int len = filter.length();\r
119         for (int i = 0; i < len;) {\r
120             char ch = filter.charAt(i);\r
121             if (DEBUG)\r
122                 System.out.println("char[" + i + "]: '" + ch + "'");\r
123 \r
124             if (ch == '"') {\r
125                 if (!inQuote) {\r
126                     if (DEBUG)\r
127                         System.out.println("begin quoted text");\r
128                     inQuote = true;\r
129                 } else {\r
130                     if (DEBUG)\r
131                         System.out.println("end quoted text");\r
132                     inQuote = false;\r
133                     addSearchWord(sb, toSinglePatternString( toString(buf), implicitPreAsterisk ));\r
134                 }\r
135                 ++i;\r
136                 continue;\r
137             } else if (ch == '\\') {\r
138                 // Next character is escaped, i.e. taken as is.\r
139                 ++i;\r
140                 if (i >= len)\r
141                     // Unexpected end-of-string\r
142                     break;\r
143 \r
144                 ch = filter.charAt(i);\r
145                 if (DEBUG)\r
146                     System.out.println("append escaped character '" + ch + "'");\r
147 \r
148                 buf.append(ch);\r
149                 ++i;\r
150                 break;\r
151             } else if (ch == ' ') {\r
152                 if (inQuote) {\r
153                     if (DEBUG)\r
154                         System.out.println("append char '" + ch + "'");\r
155                     buf.append(ch);\r
156                     ++i;\r
157                 } else {\r
158                     if (buf.position() > 0) {\r
159                         addSearchWord(sb, toSinglePatternString( toString(buf), implicitPreAsterisk ));\r
160                     }\r
161                     ++i;\r
162                 }\r
163             } else {\r
164                 if (DEBUG)\r
165                     System.out.println("append char '" + ch + "'");\r
166                 buf.append(ch);\r
167                 ++i;\r
168             }\r
169         }\r
170         if (buf.position() > 0) {\r
171             addSearchWord(sb, toSinglePatternString( toString(buf), implicitPreAsterisk ));\r
172         }\r
173 \r
174         //sb.append(".*");\r
175 \r
176         return sb.toString();\r
177     }\r
178 \r
179     @Override\r
180     public String toPatternString(String filter) {\r
181         return defaultToPatternString(filter, implicitPreAsterisk);\r
182     }\r
183 \r
184     public static Pattern compilePattern(String s) {\r
185         IFilterStrategy st = new DefaultFilterStrategy(true);\r
186         System.out.println("compilePattern(" + s + ")");\r
187         String regex = st.toPatternString(s);\r
188         System.out.println(s + " -> " + regex);\r
189         Pattern p = Pattern.compile(regex);\r
190         return p;\r
191     }\r
192 \r
193     public static void test(String pattern, String... testStrings) {\r
194         Pattern p = compilePattern(pattern);\r
195         for (String test : testStrings) {\r
196             System.out.print("\ttesting '" + test + "'");\r
197             Matcher m = p.matcher(test);\r
198             if (m.matches()) {\r
199                 System.out.println(" - MATCHES");\r
200             } else {\r
201                 System.out.println(" - NO MATCH");\r
202             }\r
203         }\r
204     }\r
205 \r
206     static String[] TEST = {\r
207         "foo bar baz biz boz",\r
208         "biz bar baz foo boz",\r
209         "foo",\r
210         "bar",\r
211         "  foo    bar  ",\r
212         "quux",\r
213         "  quux    ",\r
214         "  quux    foo",\r
215     };\r
216 \r
217     public static void main(String[] args) {\r
218         test("foo$");\r
219         test(".");\r
220         test("*");\r
221         test("?");\r
222         test("+");\r
223         test("^");\r
224         test("&");\r
225         test("&&");\r
226         test("&&&");\r
227         test("|");\r
228         test("(");\r
229         test(")");\r
230         test("[");\r
231         test("]");\r
232         test("{");\r
233         test("}");\r
234         test("()");\r
235         test("[]");\r
236         test("{}");\r
237         test("\\\\");\r
238         test("\\");\r
239 \r
240         test("foo bar", TEST);\r
241         test("\"foo bar\"", TEST);\r
242         test("\"foo bar\" quux", TEST);\r
243         test("*\"foo bar\" *quux", TEST);\r
244         test("\"*foo bar\" *quux", TEST);\r
245     }\r
246 \r
247 }\r