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