blob: 7e2db9eb2ec00546ca2c7d35d5d1a2e1298c68bb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.spelling;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.text.spelling.engine.DefaultSpellChecker;
import org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
import org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellChecker;
import org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellDictionary;
import org.eclipse.jdt.internal.ui.text.spelling.engine.LocaleSensitiveSpellDictionary;
import org.eclipse.jdt.internal.ui.text.spelling.engine.PersistentSpellDictionary;
/**
* Spell check engine for Java source spell checking.
*
* @since 3.0
*/
public class SpellCheckEngine implements ISpellCheckEngine, IPropertyChangeListener {
/** The dictionary location */
public static final String DICTIONARY_LOCATION= "dictionaries/"; //$NON-NLS-1$
/** The singleton engine instance */
private static ISpellCheckEngine fgEngine= null;
/**
* Caches the locales of installed dictionaries.
*
* @since 3.3
*/
private static Set fgLocalesWithInstalledDictionaries;
/**
* Returns the locales for which this
* spell check engine has dictionaries in certain location.
*
* @param location dictionaries location
* @return The available locales for this engine
*/
private static Set getLocalesWithInstalledDictionaries(URL location) {
String[] fileNames;
try {
URL url= FileLocator.toFileURL(location);
File file= new File(url.getFile());
if (!file.isDirectory())
return Collections.EMPTY_SET;
fileNames= file.list();
if (fileNames == null)
return Collections.EMPTY_SET;
} catch (IOException ex) {
JavaPlugin.log(ex);
return Collections.EMPTY_SET;
}
Set localesWithInstalledDictionaries= new HashSet();
int fileNameCount= fileNames.length;
for (int i= 0; i < fileNameCount; i++) {
String fileName= fileNames[i];
int localeEnd= fileName.indexOf(".dictionary"); //$NON-NLS-1$
if (localeEnd > 1) {
String localeName= fileName.substring(0, localeEnd);
int languageEnd=localeName.indexOf('_');
if (languageEnd == -1)
localesWithInstalledDictionaries.add(new Locale(localeName));
else if (languageEnd == 2 && localeName.length() == 5)
localesWithInstalledDictionaries.add(new Locale(localeName.substring(0, 2), localeName.substring(3)));
else if (localeName.length() > 6 && localeName.charAt(5) == '_')
localesWithInstalledDictionaries.add(new Locale(localeName.substring(0, 2), localeName.substring(3, 5), localeName.substring(6)));
}
}
return localesWithInstalledDictionaries;
}
/**
* Returns the locales for which this
* spell check engine has dictionaries.
*
* @return The available locales for this engine
*/
public static Set getLocalesWithInstalledDictionaries() {
if (fgLocalesWithInstalledDictionaries != null)
return fgLocalesWithInstalledDictionaries;
Enumeration locations;
try {
locations= getDictionaryLocations();
if (locations == null)
return fgLocalesWithInstalledDictionaries= Collections.EMPTY_SET;
} catch (IOException ex) {
JavaPlugin.log(ex);
return fgLocalesWithInstalledDictionaries= Collections.EMPTY_SET;
}
fgLocalesWithInstalledDictionaries= new HashSet();
while (locations.hasMoreElements()) {
URL location= (URL) locations.nextElement();
Set locales= getLocalesWithInstalledDictionaries(location);
fgLocalesWithInstalledDictionaries.addAll(locales);
}
return fgLocalesWithInstalledDictionaries;
}
/**
* Returns the default locale for this engine.
*
* @return The default locale
*/
public static Locale getDefaultLocale() {
return Locale.getDefault();
}
/**
* Returns the dictionary closest to the given locale.
*
* @param locale the locale
* @return the dictionary or <code>null</code> if none is suitable
* @since 3.3
*/
public ISpellDictionary findDictionary(Locale locale) {
ISpellDictionary dictionary= (ISpellDictionary)fLocaleDictionaries.get(locale);
if (dictionary != null)
return dictionary;
// Try same language
String language= locale.getLanguage();
Iterator iter= fLocaleDictionaries.entrySet().iterator();
while (iter.hasNext()) {
Entry entry= (Entry)iter.next();
Locale dictLocale= (Locale)entry.getKey();
if (dictLocale.getLanguage().equals(language))
return (ISpellDictionary)entry.getValue();
}
return null;
}
/*
* @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellCheckEngine#findDictionary(java.util.Locale)
* @since 3.3
*/
public static Locale findClosestLocale(Locale locale) {
if (locale == null || locale.toString().length() == 0)
return locale;
if (getLocalesWithInstalledDictionaries().contains(locale))
return locale;
// Try same language
String language= locale.getLanguage();
Iterator iter= getLocalesWithInstalledDictionaries().iterator();
while (iter.hasNext()) {
Locale dictLocale= (Locale)iter.next();
if (dictLocale.getLanguage().equals(language))
return dictLocale;
}
// Try whether American English is present
Locale defaultLocale= Locale.US;
if (getLocalesWithInstalledDictionaries().contains(defaultLocale))
return defaultLocale;
return null;
}
/**
* Returns the enumeration of URLs for the dictionary locations where
* the Platform dictionaries are located.
* <p>
* This is in <code>org.eclipse.jdt.ui/dictionaries/</code>
* which can also be populated via fragments.
* </p>
*
* @throws IOException if there is an I/O error
* @return The dictionary locations, or <code>null</code> iff the locations are not known
*/
public static Enumeration getDictionaryLocations() throws IOException {
final JavaPlugin plugin= JavaPlugin.getDefault();
if (plugin != null)
return plugin.getBundle().getResources("/" + DICTIONARY_LOCATION); //$NON-NLS-1$
return null;
}
/**
* Returns the singleton instance of the spell check engine.
*
* @return The singleton instance of the spell check engine
*/
public static final synchronized ISpellCheckEngine getInstance() {
if (fgEngine == null)
fgEngine= new SpellCheckEngine();
return fgEngine;
}
/**
* Shuts down the singleton instance of the spell check engine.
*/
public static final synchronized void shutdownInstance() {
if (fgEngine != null) {
fgEngine.shutdown();
fgEngine= null;
}
}
/** The registered locale insensitive dictionaries */
private Set fGlobalDictionaries= new HashSet();
/** The spell checker for fLocale */
private ISpellChecker fChecker= null;
/** The registered locale sensitive dictionaries */
private Map fLocaleDictionaries= new HashMap();
/** The user dictionary */
private ISpellDictionary fUserDictionary= null;
/**
* Creates a new spell check manager.
*/
private SpellCheckEngine() {
fGlobalDictionaries.add(new TaskTagDictionary());
fGlobalDictionaries.add(new HtmlTagDictionary());
fGlobalDictionaries.add(new JavaDocTagDictionary());
try {
Locale locale= null;
final Enumeration locations= getDictionaryLocations();
while (locations != null && locations.hasMoreElements()) {
URL location= (URL)locations.nextElement();
for (final Iterator iterator= getLocalesWithInstalledDictionaries(location).iterator(); iterator.hasNext();) {
locale= (Locale)iterator.next();
fLocaleDictionaries.put(locale, new LocaleSensitiveSpellDictionary(locale, location));
}
}
} catch (IOException exception) {
// Do nothing
}
JavaPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
}
/*
* @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellCheckEngine#getSpellChecker()
*/
public final synchronized ISpellChecker getSpellChecker() throws IllegalStateException {
if (fGlobalDictionaries == null)
throw new IllegalStateException("spell checker has been shut down"); //$NON-NLS-1$
IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore();
Locale locale= getCurrentLocale(store);
if (fUserDictionary == null && "".equals(locale.toString())) //$NON-NLS-1$
return null;
if (fChecker != null && fChecker.getLocale().equals(locale))
return fChecker;
resetSpellChecker();
fChecker= new DefaultSpellChecker(store, locale);
resetUserDictionary();
for (Iterator iterator= fGlobalDictionaries.iterator(); iterator.hasNext();) {
ISpellDictionary dictionary= (ISpellDictionary)iterator.next();
fChecker.addDictionary(dictionary);
}
ISpellDictionary dictionary= findDictionary(fChecker.getLocale());
if (dictionary != null)
fChecker.addDictionary(dictionary);
return fChecker;
}
/**
* Returns the current locale of the spelling preferences.
*
* @param store the preference store
* @return The current locale of the spelling preferences
*/
private Locale getCurrentLocale(IPreferenceStore store) {
return convertToLocale(store.getString(PreferenceConstants.SPELLING_LOCALE));
}
public static Locale convertToLocale(String locale) {
Locale defaultLocale= SpellCheckEngine.getDefaultLocale();
if (locale.equals(defaultLocale.toString()))
return defaultLocale;
if (locale.length() >= 5)
return new Locale(locale.substring(0, 2), locale.substring(3, 5));
return new Locale(""); //$NON-NLS-1$
}
/*
* @see org.eclipse.jdt.ui.text.spelling.engine.ISpellCheckEngine#getLocale()
*/
public synchronized final Locale getLocale() {
if (fChecker == null)
return null;
return fChecker.getLocale();
}
/*
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public final void propertyChange(final PropertyChangeEvent event) {
if (event.getProperty().equals(PreferenceConstants.SPELLING_LOCALE)) {
resetSpellChecker();
return;
}
if (event.getProperty().equals(PreferenceConstants.SPELLING_USER_DICTIONARY)) {
resetUserDictionary();
return;
}
if (event.getProperty().equals(PreferenceConstants.SPELLING_USER_DICTIONARY_ENCODING)) {
resetUserDictionary();
return;
}
}
/**
* Resets the current checker's user dictionary.
*/
private synchronized void resetUserDictionary() {
if (fChecker == null)
return;
// Update user dictionary
if (fUserDictionary != null) {
fChecker.removeDictionary(fUserDictionary);
fUserDictionary.unload();
fUserDictionary= null;
}
IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore();
final String filePath= store.getString(PreferenceConstants.SPELLING_USER_DICTIONARY);
if (filePath.length() > 0) {
try {
File file= new File(filePath);
if (!file.exists() && !file.createNewFile())
return;
final URL url= new URL("file", null, filePath); //$NON-NLS-1$
InputStream stream= url.openStream();
if (stream != null) {
try {
fUserDictionary= new PersistentSpellDictionary(url);
fChecker.addDictionary(fUserDictionary);
} finally {
stream.close();
}
}
} catch (MalformedURLException exception) {
// Do nothing
} catch (IOException exception) {
// Do nothing
}
}
}
/*
* @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellCheckEngine#registerDictionary(org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellDictionary)
*/
public synchronized final void registerGlobalDictionary(final ISpellDictionary dictionary) {
fGlobalDictionaries.add(dictionary);
resetSpellChecker();
}
/*
* @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellCheckEngine#registerDictionary(java.util.Locale, org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellDictionary)
*/
public synchronized final void registerDictionary(final Locale locale, final ISpellDictionary dictionary) {
fLocaleDictionaries.put(locale, dictionary);
resetSpellChecker();
}
/*
* @see org.eclipse.jdt.internal.ui.text.spelling.engine.ISpellCheckEngine#unload()
*/
public synchronized final void shutdown() {
JavaPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
ISpellDictionary dictionary= null;
for (final Iterator iterator= fGlobalDictionaries.iterator(); iterator.hasNext();) {
dictionary= (ISpellDictionary)iterator.next();
dictionary.unload();
}
fGlobalDictionaries= null;
for (final Iterator iterator= fLocaleDictionaries.values().iterator(); iterator.hasNext();) {
dictionary= (ISpellDictionary)iterator.next();
dictionary.unload();
}
fLocaleDictionaries= null;
fUserDictionary= null;
fChecker= null;
}
private synchronized void resetSpellChecker() {
if (fChecker != null) {
ISpellDictionary dictionary= (ISpellDictionary)fLocaleDictionaries.get(fChecker.getLocale());
if (dictionary != null)
dictionary.unload();
}
fChecker= null;
}
/*
* @see org.eclipse.jdt.ui.text.spelling.engine.ISpellCheckEngine#unregisterDictionary(org.eclipse.jdt.ui.text.spelling.engine.ISpellDictionary)
*/
public synchronized final void unregisterDictionary(final ISpellDictionary dictionary) {
fGlobalDictionaries.remove(dictionary);
fLocaleDictionaries.values().remove(dictionary);
dictionary.unload();
resetSpellChecker();
}
}