KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > spelling > engine > DefaultSpellChecker


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.internal.ui.text.spelling.engine;
13
14 import java.util.Collections JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.Locale JavaDoc;
18 import java.util.Set JavaDoc;
19
20 import org.eclipse.core.runtime.Assert;
21
22 import org.eclipse.jface.preference.IPreferenceStore;
23
24 import org.eclipse.jdt.ui.PreferenceConstants;
25
26 import org.eclipse.jdt.internal.ui.text.spelling.SpellingEngine;
27
28 /**
29  * Default spell checker for standard text.
30  *
31  * @since 3.0
32  */

33 public class DefaultSpellChecker implements ISpellChecker {
34
35     /** Array of URL prefixes */
36     public static final String JavaDoc[] URL_PREFIXES= new String JavaDoc[] { "http://", "https://", "www.", "ftp://", "ftps://", "news://", "mailto://" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
37

38     /**
39      * Does this word contain digits?
40      *
41      * @param word the word to check
42      * @return <code>true</code> iff this word contains digits, <code>false></code> otherwise
43      */

44     protected static boolean isDigits(final String JavaDoc word) {
45
46         for (int index= 0; index < word.length(); index++) {
47
48             if (Character.isDigit(word.charAt(index)))
49                 return true;
50         }
51         return false;
52     }
53
54     /**
55      * Does this word contain mixed-case letters?
56      *
57      * @param word
58      * The word to check
59      * @param sentence
60      * <code>true</code> iff the specified word starts a new
61      * sentence, <code>false</code> otherwise
62      * @return <code>true</code> iff the contains mixed-case letters, <code>false</code>
63      * otherwise
64      */

65     protected static boolean isMixedCase(final String JavaDoc word, final boolean sentence) {
66
67         final int length= word.length();
68         boolean upper= Character.isUpperCase(word.charAt(0));
69
70         if (sentence && upper && (length > 1))
71             upper= Character.isUpperCase(word.charAt(1));
72
73         if (upper) {
74
75             for (int index= length - 1; index > 0; index--) {
76                 if (Character.isLowerCase(word.charAt(index)))
77                     return true;
78             }
79         } else {
80
81             for (int index= length - 1; index > 0; index--) {
82                 if (Character.isUpperCase(word.charAt(index)))
83                     return true;
84             }
85         }
86         return false;
87     }
88
89     /**
90      * Does this word contain upper-case letters only?
91      *
92      * @param word
93      * The word to check
94      * @return <code>true</code> iff this word only contains upper-case
95      * letters, <code>false</code> otherwise
96      */

97     protected static boolean isUpperCase(final String JavaDoc word) {
98
99         for (int index= word.length() - 1; index >= 0; index--) {
100
101             if (Character.isLowerCase(word.charAt(index)))
102                 return false;
103         }
104         return true;
105     }
106
107     /**
108      * Does this word look like an URL?
109      *
110      * @param word
111      * The word to check
112      * @return <code>true</code> iff this word looks like an URL, <code>false</code>
113      * otherwise
114      */

115     protected static boolean isUrl(final String JavaDoc word) {
116
117         for (int index= 0; index < URL_PREFIXES.length; index++) {
118
119             if (word.startsWith(URL_PREFIXES[index]))
120                 return true;
121         }
122         return false;
123     }
124
125     /**
126      * The dictionaries to use for spell checking. Synchronized to avoid
127      * concurrent modifications.
128      */

129     private final Set JavaDoc fDictionaries= Collections.synchronizedSet(new HashSet JavaDoc());
130
131     /**
132      * The words to be ignored. Synchronized to avoid concurrent modifications.
133      */

134     private final Set JavaDoc fIgnored= Collections.synchronizedSet(new HashSet JavaDoc());
135
136     /**
137      * The spell event listeners. Synchronized to avoid concurrent
138      * modifications.
139      */

140     private final Set JavaDoc fListeners= Collections.synchronizedSet(new HashSet JavaDoc());
141
142     /**
143      * The preference store. Assumes the <code>IPreferenceStore</code>
144      * implementation is thread safe.
145      */

146     private final IPreferenceStore fPreferences;
147
148     /**
149      * The locale of this checker.
150      * @since 3.3
151      */

152     private Locale JavaDoc fLocale;
153
154     /**
155      * Creates a new default spell checker.
156      *
157      * @param store the preference store for this spell checker
158      * @param locale the locale
159      */

160     public DefaultSpellChecker(IPreferenceStore store, Locale JavaDoc locale) {
161         Assert.isLegal(store != null);
162         Assert.isLegal(locale != null);
163         
164         fPreferences= store;
165         fLocale= locale;
166     }
167
168     /*
169      * @see org.eclipse.spelling.done.ISpellChecker#addDictionary(org.eclipse.spelling.done.ISpellDictionary)
170      */

171     public final void addDictionary(final ISpellDictionary dictionary) {
172         // synchronizing is necessary as this is a write access
173
fDictionaries.add(dictionary);
174     }
175
176     /*
177      * @see org.eclipse.spelling.done.ISpellChecker#addListener(org.eclipse.spelling.done.ISpellEventListener)
178      */

179     public final void addListener(final ISpellEventListener listener) {
180         // synchronizing is necessary as this is a write access
181
fListeners.add(listener);
182     }
183
184     /*
185      * @see org.eclipse.jdt.ui.text.spelling.engine.ISpellChecker#acceptsWords()
186      */

187     public boolean acceptsWords() {
188         // synchronizing might not be needed here since acceptWords is
189
// a read-only access and only called in the same thread as
190
// the modifying methods add/checkWord (?)
191
Set JavaDoc copy;
192         synchronized (fDictionaries) {
193             copy= new HashSet JavaDoc(fDictionaries);
194         }
195
196         ISpellDictionary dictionary= null;
197         for (final Iterator JavaDoc iterator= copy.iterator(); iterator.hasNext();) {
198
199             dictionary= (ISpellDictionary)iterator.next();
200             if (dictionary.acceptsWords())
201                 return true;
202         }
203         return false;
204     }
205
206     /*
207      * @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellChecker#addWord(java.lang.String)
208      */

209     public void addWord(final String JavaDoc word) {
210         // synchronizing is necessary as this is a write access
211
Set JavaDoc copy;
212         synchronized (fDictionaries) {
213             copy= new HashSet JavaDoc(fDictionaries);
214         }
215
216         final String JavaDoc addable= word.toLowerCase();
217         for (final Iterator JavaDoc iterator= copy.iterator(); iterator.hasNext();) {
218             ISpellDictionary dictionary= (ISpellDictionary)iterator.next();
219             if (dictionary.acceptsWords())
220                 dictionary.addWord(addable);
221         }
222     
223     }
224
225     /*
226      * @see org.eclipse.jdt.ui.text.spelling.engine.ISpellChecker#checkWord(java.lang.String)
227      */

228     public final void checkWord(final String JavaDoc word) {
229         // synchronizing is necessary as this is a write access
230
fIgnored.remove(word.toLowerCase());
231     }
232
233     /*
234      * @see org.eclipse.spelling.done.ISpellChecker#execute(org.eclipse.spelling.ISpellCheckTokenizer)
235      */

236     public void execute(final ISpellCheckIterator iterator) {
237
238         final boolean ignoreDigits= fPreferences.getBoolean(PreferenceConstants.SPELLING_IGNORE_DIGITS);
239         final boolean ignoreMixed= fPreferences.getBoolean(PreferenceConstants.SPELLING_IGNORE_MIXED);
240         final boolean ignoreSentence= fPreferences.getBoolean(PreferenceConstants.SPELLING_IGNORE_SENTENCE);
241         final boolean ignoreUpper= fPreferences.getBoolean(PreferenceConstants.SPELLING_IGNORE_UPPER);
242         final boolean ignoreURLS= fPreferences.getBoolean(PreferenceConstants.SPELLING_IGNORE_URLS);
243         final boolean ignoreNonLetters= fPreferences.getBoolean(PreferenceConstants.SPELLING_IGNORE_NON_LETTERS);
244         final boolean ignoreSingleLetters= fPreferences.getBoolean(PreferenceConstants.SPELLING_IGNORE_SINGLE_LETTERS);
245         final int problemsThreshold= PreferenceConstants.getPreferenceStore().getInt(SpellingEngine.SPELLING_PROBLEMS_THRESHOLD);
246         
247         iterator.setIgnoreSingleLetters(ignoreSingleLetters);
248         
249         Iterator JavaDoc iter= fDictionaries.iterator();
250         while (iter.hasNext())
251             ((ISpellDictionary)iter.next()).setStripNonLetters(ignoreNonLetters);
252
253         String JavaDoc word= null;
254         boolean starts= false;
255         int problemCount= 0;
256
257         while (problemCount <= problemsThreshold && iterator.hasNext()) {
258
259             word= (String JavaDoc)iterator.next();
260             if (word != null) {
261
262                 // synchronizing is necessary as this is called inside the reconciler
263
if (!fIgnored.contains(word)) {
264
265                     starts= iterator.startsSentence();
266                     if (!isCorrect(word)) {
267
268                         boolean isMixed= isMixedCase(word, true);
269                         boolean isUpper= isUpperCase(word);
270                         boolean isDigits= isDigits(word);
271                         boolean isURL= isUrl(word);
272
273                         if ( !ignoreMixed && isMixed || !ignoreUpper && isUpper || !ignoreDigits && isDigits || !ignoreURLS && isURL || !(isMixed || isUpper || isDigits || isURL)) {
274                             fireEvent(new SpellEvent(this, word, iterator.getBegin(), iterator.getEnd(), starts, false));
275                             problemCount++;
276                         }
277
278                     } else {
279
280                         if (!ignoreSentence && starts && Character.isLowerCase(word.charAt(0))) {
281                             fireEvent(new SpellEvent(this, word, iterator.getBegin(), iterator.getEnd(), true, true));
282                             problemCount++;
283                         }
284                     }
285                 }
286             }
287         }
288     }
289
290     /**
291      * Fires the specified event.
292      *
293      * @param event
294      * Event to fire
295      */

296     protected final void fireEvent(final ISpellEvent event) {
297         // synchronizing is necessary as this is called from execute
298
Set JavaDoc copy;
299         synchronized (fListeners) {
300             copy= new HashSet JavaDoc(fListeners);
301         }
302         for (final Iterator JavaDoc iterator= copy.iterator(); iterator.hasNext();) {
303             ((ISpellEventListener)iterator.next()).handle(event);
304         }
305     }
306
307     /*
308      * @see org.eclipse.spelling.done.ISpellChecker#getProposals(java.lang.String,boolean)
309      */

310     public Set JavaDoc getProposals(final String JavaDoc word, final boolean sentence) {
311
312         // synchronizing might not be needed here since getProposals is
313
// a read-only access and only called in the same thread as
314
// the modifing methods add/removeDictionary (?)
315
Set JavaDoc copy;
316         synchronized (fDictionaries) {
317             copy= new HashSet JavaDoc(fDictionaries);
318         }
319
320         ISpellDictionary dictionary= null;
321         final HashSet JavaDoc proposals= new HashSet JavaDoc();
322
323         for (final Iterator JavaDoc iterator= copy.iterator(); iterator.hasNext();) {
324
325             dictionary= (ISpellDictionary)iterator.next();
326             proposals.addAll(dictionary.getProposals(word, sentence));
327         }
328         return proposals;
329     }
330
331     /*
332      * @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellChecker#ignoreWord(java.lang.String)
333      */

334     public final void ignoreWord(final String JavaDoc word) {
335         // synchronizing is necessary as this is a write access
336
fIgnored.add(word.toLowerCase());
337     }
338
339     /*
340      * @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellChecker#isCorrect(java.lang.String)
341      */

342     public final boolean isCorrect(final String JavaDoc word) {
343         // synchronizing is necessary as this is called from execute
344
Set JavaDoc copy;
345         synchronized (fDictionaries) {
346             copy= new HashSet JavaDoc(fDictionaries);
347         }
348
349         if (fIgnored.contains(word.toLowerCase()))
350             return true;
351
352         ISpellDictionary dictionary= null;
353         for (final Iterator JavaDoc iterator= copy.iterator(); iterator.hasNext();) {
354
355             dictionary= (ISpellDictionary)iterator.next();
356             if (dictionary.isCorrect(word))
357                 return true;
358         }
359         return false;
360     }
361
362     /*
363      * @see org.eclipse.spelling.done.ISpellChecker#removeDictionary(org.eclipse.spelling.done.ISpellDictionary)
364      */

365     public final void removeDictionary(final ISpellDictionary dictionary) {
366         // synchronizing is necessary as this is a write access
367
fDictionaries.remove(dictionary);
368     }
369
370     /*
371      * @see org.eclipse.spelling.done.ISpellChecker#removeListener(org.eclipse.spelling.done.ISpellEventListener)
372      */

373     public final void removeListener(final ISpellEventListener listener) {
374         // synchronizing is necessary as this is a write access
375
fListeners.remove(listener);
376     }
377
378     /*
379      * @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellChecker#getLocale()
380      * @since 3.3
381      */

382     public Locale JavaDoc getLocale() {
383         return fLocale;
384     }
385 }
386
Popular Tags