1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.fulltext; |
7 | |
8 | import java.sql.Connection; |
9 | import java.sql.PreparedStatement; |
10 | import java.sql.ResultSet; |
11 | import java.sql.SQLException; |
12 | import java.sql.Statement; |
13 | import java.util.HashMap; |
14 | import java.util.HashSet; |
15 | import org.h2.util.New; |
16 | import org.h2.util.SoftHashMap; |
17 | |
18 | /** |
19 | * The global settings of a full text search. |
20 | */ |
21 | class FullTextSettings { |
22 | |
23 | /** |
24 | * The settings of open indexes. |
25 | */ |
26 | private static final HashMap<String, FullTextSettings> SETTINGS = New.hashMap(); |
27 | |
28 | /** |
29 | * Whether this instance has been initialized. |
30 | */ |
31 | private boolean initialized; |
32 | |
33 | /** |
34 | * The set of words not to index (stop words). |
35 | */ |
36 | private final HashSet<String> ignoreList = New.hashSet(); |
37 | |
38 | /** |
39 | * The set of words / terms. |
40 | */ |
41 | private final HashMap<String, Integer> words = New.hashMap(); |
42 | |
43 | /** |
44 | * The set of indexes in this database. |
45 | */ |
46 | private final HashMap<Integer, IndexInfo> indexes = New.hashMap(); |
47 | |
48 | /** |
49 | * The prepared statement cache. |
50 | */ |
51 | private final SoftHashMap<Connection, |
52 | SoftHashMap<String, PreparedStatement>> cache = |
53 | new SoftHashMap<Connection, SoftHashMap<String, PreparedStatement>>(); |
54 | |
55 | /** |
56 | * The whitespace characters. |
57 | */ |
58 | private String whitespaceChars = " \t\n\r\f+\"*%&/()=?'!,.;:-_#@|^~`{}[]<>\\"; |
59 | |
60 | /** |
61 | * Create a new instance. |
62 | */ |
63 | protected FullTextSettings() { |
64 | // don't allow construction |
65 | } |
66 | |
67 | /** |
68 | * Get the ignore list. |
69 | * |
70 | * @return the ignore list |
71 | */ |
72 | protected HashSet<String> getIgnoreList() { |
73 | return ignoreList; |
74 | } |
75 | |
76 | /** |
77 | * Get the word list. |
78 | * |
79 | * @return the word list |
80 | */ |
81 | protected HashMap<String, Integer> getWordList() { |
82 | return words; |
83 | } |
84 | |
85 | /** |
86 | * Get the index information for the given index id. |
87 | * |
88 | * @param indexId the index id |
89 | * @return the index info |
90 | */ |
91 | protected IndexInfo getIndexInfo(int indexId) { |
92 | return indexes.get(indexId); |
93 | } |
94 | |
95 | /** |
96 | * Add an index. |
97 | * |
98 | * @param index the index |
99 | */ |
100 | protected void addIndexInfo(IndexInfo index) { |
101 | indexes.put(index.id, index); |
102 | } |
103 | |
104 | /** |
105 | * Convert a word to uppercase. This method returns null if the word is in |
106 | * the ignore list. |
107 | * |
108 | * @param word the word to convert and check |
109 | * @return the uppercase version of the word or null |
110 | */ |
111 | protected String convertWord(String word) { |
112 | // TODO this is locale specific, document |
113 | word = word.toUpperCase(); |
114 | if (ignoreList.contains(word)) { |
115 | return null; |
116 | } |
117 | return word; |
118 | } |
119 | |
120 | /** |
121 | * Get or create the fulltext settings for this database. |
122 | * |
123 | * @param conn the connection |
124 | * @return the settings |
125 | */ |
126 | protected static FullTextSettings getInstance(Connection conn) |
127 | throws SQLException { |
128 | String path = getIndexPath(conn); |
129 | FullTextSettings setting = SETTINGS.get(path); |
130 | if (setting == null) { |
131 | setting = new FullTextSettings(); |
132 | SETTINGS.put(path, setting); |
133 | } |
134 | return setting; |
135 | } |
136 | |
137 | /** |
138 | * Get the file system path. |
139 | * |
140 | * @param conn the connection |
141 | * @return the file system path |
142 | */ |
143 | protected static String getIndexPath(Connection conn) throws SQLException { |
144 | Statement stat = conn.createStatement(); |
145 | ResultSet rs = stat.executeQuery( |
146 | "CALL IFNULL(DATABASE_PATH(), 'MEM:' || DATABASE())"); |
147 | rs.next(); |
148 | String path = rs.getString(1); |
149 | if ("MEM:UNNAMED".equals(path)) { |
150 | throw FullText.throwException( |
151 | "Fulltext search for private (unnamed) " + |
152 | "in-memory databases is not supported."); |
153 | } |
154 | rs.close(); |
155 | return path; |
156 | } |
157 | |
158 | /** |
159 | * Prepare a statement. The statement is cached in a soft reference cache. |
160 | * |
161 | * @param conn the connection |
162 | * @param sql the statement |
163 | * @return the prepared statement |
164 | */ |
165 | protected synchronized PreparedStatement prepare(Connection conn, String sql) |
166 | throws SQLException { |
167 | SoftHashMap<String, PreparedStatement> c = cache.get(conn); |
168 | if (c == null) { |
169 | c = new SoftHashMap<String, PreparedStatement>(); |
170 | cache.put(conn, c); |
171 | } |
172 | PreparedStatement prep = c.get(sql); |
173 | if (prep != null && prep.getConnection().isClosed()) { |
174 | prep = null; |
175 | } |
176 | if (prep == null) { |
177 | prep = conn.prepareStatement(sql); |
178 | c.put(sql, prep); |
179 | } |
180 | return prep; |
181 | } |
182 | |
183 | /** |
184 | * Remove all indexes from the settings. |
185 | */ |
186 | protected void removeAllIndexes() { |
187 | indexes.clear(); |
188 | } |
189 | |
190 | /** |
191 | * Remove an index from the settings. |
192 | * |
193 | * @param index the index to remove |
194 | */ |
195 | protected void removeIndexInfo(IndexInfo index) { |
196 | indexes.remove(index.id); |
197 | } |
198 | |
199 | /** |
200 | * Set the initialized flag. |
201 | * |
202 | * @param b the new value |
203 | */ |
204 | protected void setInitialized(boolean b) { |
205 | this.initialized = b; |
206 | } |
207 | |
208 | /** |
209 | * Get the initialized flag. |
210 | * |
211 | * @return whether this instance is initialized |
212 | */ |
213 | protected boolean isInitialized() { |
214 | return initialized; |
215 | } |
216 | |
217 | /** |
218 | * Close all fulltext settings, freeing up memory. |
219 | */ |
220 | protected static void closeAll() { |
221 | SETTINGS.clear(); |
222 | } |
223 | |
224 | protected void setWhitespaceChars(String whitespaceChars) { |
225 | this.whitespaceChars = whitespaceChars; |
226 | } |
227 | |
228 | protected String getWhitespaceChars() { |
229 | return whitespaceChars; |
230 | } |
231 | |
232 | } |