001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.search;
016    
017    import com.liferay.portal.kernel.configuration.Filter;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.nio.charset.CharsetEncoderUtil;
021    import com.liferay.portal.kernel.util.ArrayUtil;
022    import com.liferay.portal.kernel.util.Base64;
023    import com.liferay.portal.kernel.util.Digester;
024    import com.liferay.portal.kernel.util.DigesterUtil;
025    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
026    import com.liferay.portal.kernel.util.PropsKeys;
027    import com.liferay.portal.kernel.util.PropsUtil;
028    import com.liferay.portal.kernel.util.StreamUtil;
029    import com.liferay.portal.kernel.util.StringBundler;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.model.Group;
033    import com.liferay.portal.service.GroupLocalServiceUtil;
034    import com.liferay.portal.util.PortletKeys;
035    
036    import java.io.InputStream;
037    
038    import java.net.URL;
039    
040    import java.nio.CharBuffer;
041    import java.nio.charset.CharsetEncoder;
042    
043    import java.util.List;
044    
045    /**
046     * @author Michael C. Han
047     */
048    public abstract class BaseSpellCheckIndexWriter
049            implements SpellCheckIndexWriter {
050    
051            @Override
052            public void indexQuerySuggestionDictionaries(SearchContext searchContext)
053                    throws SearchException {
054    
055                    try {
056                            for (String languageId : _SUPPORTED_LOCALES) {
057                                    indexKeywords(
058                                            searchContext.getCompanyId(), languageId,
059                                            PropsKeys.INDEX_SEARCH_QUERY_SUGGESTION_DICTIONARY,
060                                            Field.KEYWORD_SEARCH,
061                                            SuggestionConstants.TYPE_QUERY_SUGGESTION, 0);
062                            }
063                    }
064                    catch (Exception e) {
065                            throw new SearchException(e);
066                    }
067            }
068    
069            @Override
070            public void indexQuerySuggestionDictionary(SearchContext searchContext)
071                    throws SearchException {
072    
073                    try {
074                            indexKeywords(
075                                    searchContext.getCompanyId(), searchContext.getLanguageId(),
076                                    PropsKeys.INDEX_SEARCH_QUERY_SUGGESTION_DICTIONARY,
077                                    Field.KEYWORD_SEARCH, SuggestionConstants.TYPE_QUERY_SUGGESTION,
078                                    0);
079                    }
080                    catch (Exception e) {
081                            throw new SearchException(e);
082                    }
083            }
084    
085            @Override
086            public void indexSpellCheckerDictionaries(SearchContext searchContext)
087                    throws SearchException {
088    
089                    try {
090                            for (String languageId : _SUPPORTED_LOCALES) {
091                                    indexKeywords(
092                                            searchContext.getCompanyId(), languageId,
093                                            PropsKeys.INDEX_SEARCH_SPELL_CHECKER_DICTIONARY,
094                                            Field.SPELL_CHECK_WORD,
095                                            SuggestionConstants.TYPE_SPELL_CHECKER, 0);
096                            }
097                    }
098                    catch (Exception e) {
099                            throw new SearchException(e);
100                    }
101            }
102    
103            @Override
104            public void indexSpellCheckerDictionary(SearchContext searchContext)
105                    throws SearchException {
106    
107                    try {
108                            indexKeywords(
109                                    searchContext.getCompanyId(), searchContext.getLanguageId(),
110                                    PropsKeys.INDEX_SEARCH_SPELL_CHECKER_DICTIONARY,
111                                    Field.SPELL_CHECK_WORD, SuggestionConstants.TYPE_SPELL_CHECKER,
112                                    0);
113                    }
114                    catch (Exception e) {
115                            throw new SearchException(e);
116                    }
117            }
118    
119            protected URL getResource(String name) {
120                    ClassLoader contextClassLoader =
121                            Thread.currentThread().getContextClassLoader();
122    
123                    URL url = contextClassLoader.getResource(name);
124    
125                    if (url == null) {
126                            ClassLoader portalClassLoader =
127                                    PortalClassLoaderUtil.getClassLoader();
128    
129                            url = portalClassLoader.getResource(name);
130                    }
131    
132                    return url;
133            }
134    
135            protected String getUID(
136                    long companyId, String languageId, String word, String... parameters) {
137    
138                    StringBundler uidSB = new StringBundler();
139    
140                    uidSB.append(String.valueOf(companyId));
141                    uidSB.append(StringPool.UNDERLINE);
142                    uidSB.append(PortletKeys.SEARCH);
143                    uidSB.append(_PORTLET_SEPARATOR);
144    
145                    int length = 4;
146    
147                    if (parameters != null) {
148                            length += parameters.length;
149                    }
150    
151                    try {
152                            CharsetEncoder charsetEncoder =
153                                    CharsetEncoderUtil.getCharsetEncoder(StringPool.UTF8);
154    
155                            StringBundler keySB = new StringBundler(length);
156    
157                            keySB.append(languageId);
158                            keySB.append(StringPool.UNDERLINE);
159                            keySB.append(word);
160                            keySB.append(StringPool.UNDERLINE);
161    
162                            keySB.append(StringUtil.toLowerCase(word));
163    
164                            if (parameters != null) {
165                                    for (String parameter : parameters) {
166                                            keySB.append(parameter);
167                                            keySB.append(StringPool.UNDERLINE);
168                                    }
169                            }
170    
171                            String key = keySB.toString();
172    
173                            byte[] bytes = DigesterUtil.digestRaw(
174                                    Digester.MD5, charsetEncoder.encode(CharBuffer.wrap(key)));
175    
176                            uidSB.append(Base64.encode(bytes));
177                    }
178                    catch (Exception e) {
179                            throw new IllegalStateException(e);
180                    }
181    
182                    return uidSB.toString();
183            }
184    
185            protected abstract void indexKeywords(
186                            long companyId, long groupId, String languageId,
187                            InputStream inputStream, String keywordFieldName,
188                            String typeFieldValue, int maxNGramLength)
189                    throws Exception;
190    
191            protected void indexKeywords(
192                            long companyId, long groupId, String languageId,
193                            String[] dictionaryFileNames, String keywordFieldName,
194                            String typeFieldValue, int maxNGramLength)
195                    throws Exception {
196    
197                    for (String dictionaryFileName : dictionaryFileNames) {
198                            InputStream inputStream = null;
199    
200                            if (_log.isInfoEnabled()) {
201                                    _log.info(
202                                            "Start indexing dictionary for " + dictionaryFileName);
203                            }
204    
205                            try {
206                                    URL url = getResource(dictionaryFileName);
207    
208                                    if (url == null) {
209                                            if (_log.isWarnEnabled()) {
210                                                    _log.warn("Unable to read " + dictionaryFileName);
211                                            }
212    
213                                            continue;
214                                    }
215    
216                                    inputStream = url.openStream();
217    
218                                    if (inputStream == null) {
219                                            if (_log.isWarnEnabled()) {
220                                                    _log.warn("Unable to read " + dictionaryFileName);
221                                            }
222    
223                                            continue;
224                                    }
225    
226                                    indexKeywords(
227                                            companyId, groupId, languageId, inputStream,
228                                            keywordFieldName, typeFieldValue, maxNGramLength);
229                            }
230                            finally {
231                                    StreamUtil.cleanUp(inputStream);
232                            }
233    
234                            if (_log.isInfoEnabled()) {
235                                    _log.info(
236                                            "Finished indexing dictionary for " + dictionaryFileName);
237                            }
238                    }
239            }
240    
241            protected void indexKeywords(
242                            long companyId, String languageId, String propsKey,
243                            String keywordFieldName, String typeFieldValue, int maxNGramLength)
244                    throws Exception {
245    
246                    String[] dictionaryFileNames = PropsUtil.getArray(
247                            propsKey, new Filter(languageId));
248    
249                    indexKeywords(
250                            companyId, 0, languageId, dictionaryFileNames, keywordFieldName,
251                            typeFieldValue, maxNGramLength);
252    
253                    List<Group> groups = GroupLocalServiceUtil.getLiveGroups();
254    
255                    for (Group group : groups) {
256                            String[] groupDictionaryFileNames = PropsUtil.getArray(
257                                    PropsKeys.INDEX_SEARCH_SPELL_CHECKER_DICTIONARY,
258                                    new Filter(languageId, String.valueOf(group.getGroupId())));
259    
260                            if (ArrayUtil.isEmpty(groupDictionaryFileNames)) {
261                                    continue;
262                            }
263    
264                            indexKeywords(
265                                    companyId, group.getGroupId(), languageId, dictionaryFileNames,
266                                    keywordFieldName, typeFieldValue, maxNGramLength);
267                    }
268            }
269    
270            private static final String _PORTLET_SEPARATOR = "_PORTLET_";
271    
272            private static final String[] _SUPPORTED_LOCALES = StringUtil.split(
273                    PropsUtil.get(PropsKeys.INDEX_SEARCH_SPELL_CHECKER_SUPPORTED_LOCALES));
274    
275            private static Log _log = LogFactoryUtil.getLog(
276                    BaseSpellCheckIndexWriter.class);
277    
278    }