/******************************************************************************* * 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 com.aptana.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.ui.editors.text.EditorsUI; import com.aptana.internal.ui.text.spelling.engine.DefaultSpellChecker; import com.aptana.internal.ui.text.spelling.engine.ISpellCheckEngine; import com.aptana.internal.ui.text.spelling.engine.ISpellChecker; import com.aptana.internal.ui.text.spelling.engine.ISpellDictionary; import com.aptana.internal.ui.text.spelling.engine.LocaleSensitiveSpellDictionary; import com.aptana.internal.ui.text.spelling.engine.PersistentSpellDictionary; import com.aptana.semantic.ui.text.spelling.Activator; /** * 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 = "dicts/"; //$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 { final URL url = FileLocator.toFileURL(location); final File file = new File(url.getFile()); if (!file.isDirectory()) { return Collections.EMPTY_SET; } fileNames = file.list(); if (fileNames == null) { return Collections.EMPTY_SET; } } catch (final IOException ex) { Activator.log(ex); return Collections.EMPTY_SET; } final Set localesWithInstalledDictionaries = new HashSet(); final int fileNameCount = fileNames.length; for (int i = 0; i < fileNameCount; i++) { final String fileName = fileNames[i]; final int localeEnd = fileName.indexOf(".dictionary"); //$NON-NLS-1$ if (localeEnd > 1) { final String localeName = fileName.substring(0, localeEnd); final 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 (final IOException ex) { Activator.log(ex); return fgLocalesWithInstalledDictionaries = Collections.EMPTY_SET; } fgLocalesWithInstalledDictionaries = new HashSet(); while (locations.hasMoreElements()) { final URL location = (URL) locations.nextElement(); final 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) { final ISpellDictionary dictionary = (ISpellDictionary) this.fLocaleDictionaries .get(locale); if (dictionary != null) { return dictionary; } // Try same language final String language = locale.getLanguage(); final Iterator iter = this.fLocaleDictionaries.entrySet().iterator(); while (iter.hasNext()) { final Entry entry = (Entry) iter.next(); final Locale dictLocale = (Locale) entry.getKey(); if (dictLocale.getLanguage().equals(language)) { return (ISpellDictionary) entry.getValue(); } } final ISpellDictionary next = (ISpellDictionary) this.fLocaleDictionaries .values().iterator().next(); return next; } /* * @seecom.onpositive.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 final String language = locale.getLanguage(); final Iterator iter = getLocalesWithInstalledDictionaries().iterator(); while (iter.hasNext()) { final Locale dictLocale = (Locale) iter.next(); if (dictLocale.getLanguage().equals(language)) { return dictLocale; } } // Try whether American English is present final 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 Activator plugin = Activator.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()) { final URL location = (URL) locations.nextElement(); for (final Iterator iterator = getLocalesWithInstalledDictionaries( location).iterator(); iterator.hasNext();) { locale = (Locale) iterator.next(); this.fLocaleDictionaries .put(locale, new LocaleSensitiveSpellDictionary( locale, location)); } } } catch (final IOException exception) { // Do nothing } Activator.getSpellingPreferenceStore().addPropertyChangeListener(this); } /* * @seecom.onpositive.internal.ui.text.spelling.engine.ISpellCheckEngine# * getSpellChecker() */ public final synchronized ISpellChecker getSpellChecker() throws IllegalStateException { if (this.fGlobalDictionaries == null) { throw new IllegalStateException("spell checker has been shut down"); //$NON-NLS-1$ } final IPreferenceStore store = Activator.getSpellingPreferenceStore(); final Locale locale = this.getCurrentLocale(store); if ((this.fUserDictionary == null) && "".equals(locale.toString())) { //$NON-NLS-1$ return null; } if ((this.fChecker != null) && this.fChecker.getLocale().equals(locale)) { return this.fChecker; } this.resetSpellChecker(); this.fChecker = new DefaultSpellChecker(store, locale); this.resetUserDictionary(); for (final Iterator iterator = this.fGlobalDictionaries.iterator(); iterator .hasNext();) { final ISpellDictionary dictionary = (ISpellDictionary) iterator .next(); this.fChecker.addDictionary(dictionary); } final ISpellDictionary dictionary = this.findDictionary(this.fChecker .getLocale()); if (dictionary != null) { this.fChecker.addDictionary(dictionary); } return this.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 (Locale.getDefault());// store.getString(PreferenceConstants. // SPELLING_LOCALE)); } public static Locale convertToLocale(String locale) { final Locale defaultLocale = SpellCheckEngine.getDefaultLocale(); if ((locale == null) || 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 (this.fChecker == null) { return null; } return this.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)) { this.resetSpellChecker(); return; } if (event.getProperty().equals( PreferenceConstants.SPELLING_USER_DICTIONARY)) { this.resetUserDictionary(); return; } if (event.getProperty().equals( PreferenceConstants.SPELLING_USER_DICTIONARY_ENCODING)) { this.resetUserDictionary(); return; } } /** * Resets the current checker's user dictionary. */ private synchronized void resetUserDictionary() { if (this.fChecker == null) { return; } // Update user dictionary if (this.fUserDictionary != null) { this.fChecker.removeDictionary(this.fUserDictionary); this.fUserDictionary.unload(); this.fUserDictionary = null; } final IPreferenceStore store = Activator.getSpellingPreferenceStore(); // Activator.getSpellingPreferenceStore()(); final String filePath = store .getString(PreferenceConstants.SPELLING_USER_DICTIONARY); // IStringVariableManager variableManager= // VariablesPlugin.getDefault().getStringVariableManager(); // try { // filePath= variableManager.performStringSubstitution(filePath); // } catch (CoreException e) { // JavaPlugin.log(e); // return; // } if (filePath.length() > 0) { try { final File file = new File(filePath); if (!file.exists() && !file.createNewFile()) { return; } final URL url = new URL("file", null, filePath); //$NON-NLS-1$ final InputStream stream = url.openStream(); if (stream != null) { try { this.fUserDictionary = new PersistentSpellDictionary( url); this.fChecker.addDictionary(this.fUserDictionary); } finally { stream.close(); } } } catch (final MalformedURLException exception) { // Do nothing } catch (final IOException exception) { // Do nothing } } } /* * @seecom.onpositive.internal.ui.text.spelling.engine.ISpellCheckEngine# * registerDictionary * (com.onpositive.internal.ui.text.spelling.engine.ISpellDictionary) */ public synchronized final void registerGlobalDictionary( final ISpellDictionary dictionary) { this.fGlobalDictionaries.add(dictionary); this.resetSpellChecker(); } /* * @seecom.onpositive.internal.ui.text.spelling.engine.ISpellCheckEngine# * registerDictionary(java.util.Locale, * com.onpositive.internal.ui.text.spelling.engine.ISpellDictionary) */ public synchronized final void registerDictionary(final Locale locale, final ISpellDictionary dictionary) { this.fLocaleDictionaries.put(locale, dictionary); this.resetSpellChecker(); } /* * @see * com.onpositive.internal.ui.text.spelling.engine.ISpellCheckEngine#unload * () */ public synchronized final void shutdown() { Activator.getSpellingPreferenceStore().removePropertyChangeListener(this); ISpellDictionary dictionary = null; for (final Iterator iterator = this.fGlobalDictionaries.iterator(); iterator .hasNext();) { dictionary = (ISpellDictionary) iterator.next(); dictionary.unload(); } this.fGlobalDictionaries = null; for (final Iterator iterator = this.fLocaleDictionaries.values() .iterator(); iterator.hasNext();) { dictionary = (ISpellDictionary) iterator.next(); dictionary.unload(); } this.fLocaleDictionaries = null; this.fUserDictionary = null; this.fChecker = null; } private synchronized void resetSpellChecker() { if (this.fChecker != null) { final ISpellDictionary dictionary = (ISpellDictionary) this.fLocaleDictionaries .get(this.fChecker.getLocale()); if (dictionary != null) { dictionary.unload(); } } this.fChecker = null; } /* * @seeorg.eclipse.jdt.ui.text.spelling.engine.ISpellCheckEngine# * unregisterDictionary * (org.eclipse.jdt.ui.text.spelling.engine.ISpellDictionary) */ public synchronized final void unregisterDictionary( final ISpellDictionary dictionary) { this.fGlobalDictionaries.remove(dictionary); this.fLocaleDictionaries.values().remove(dictionary); dictionary.unload(); this.resetSpellChecker(); } }