Java tutorial
/** * Aptana Studio * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.theme.internal; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Properties; import java.util.Set; import org.eclipse.core.internal.preferences.Base64; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.resource.DataFormatException; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.StringConverter; import org.eclipse.jface.text.TextAttribute; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.Token; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.RGB; import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; import org.eclipse.ui.texteditor.AbstractTextEditor; import org.eclipse.ui.texteditor.AnnotationPreference; import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; import org.osgi.framework.Bundle; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import com.aptana.core.logging.IdeLog; import com.aptana.core.util.CollectionsUtil; import com.aptana.core.util.EclipseUtil; import com.aptana.core.util.IOUtil; import com.aptana.core.util.StringUtil; import com.aptana.scope.ScopeSelector; import com.aptana.theme.IThemeManager; import com.aptana.theme.Theme; import com.aptana.theme.ThemePlugin; import com.aptana.theme.ThemeRule; import com.aptana.theme.internal.preferences.ThemerPreferenceInitializer; import com.aptana.theme.preferences.IPreferenceConstants; import com.aptana.ui.util.UIUtils; @SuppressWarnings("restriction") public class ThemeManager implements IThemeManager { /** * Node in preferences used to store themes under. Each theme is a key value pair under this node. The key is the * theme name, value is XML format java Properties object. */ public static final String THEMES_NODE = "themes"; //$NON-NLS-1$ // TODO Don't expose this node name. Fold saving/loading of themes into this impl private volatile Theme fCurrentTheme; private Set<String> fBuiltins; private Set<String> fThemeNames; private static ThemeManager fgInstance; /** * The common prefixes of prefs related to annotations that we typically modify */ private static final String[] annotationKeyPrefixes = new String[] { "pydevOccurrenceIndication", //$NON-NLS-1$ "searchResultIndication", //$NON-NLS-1$ "xmlTagPairOccurrenceIndication", //$NON-NLS-1$ "htmlTagPairOccurrenceIndication", //$NON-NLS-1$ "rubyBlockPairOccurrenceIndication", //$NON-NLS-1$ }; private ThemeManager() { EclipseUtil.instanceScope().getNode("org.eclipse.ui.editors").addPreferenceChangeListener( //$NON-NLS-1$ new IPreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent event) { // Listen to see if the user is modifying the annotations through Annotations pref page for (String prefix : annotationKeyPrefixes) { if (event.getKey().startsWith(prefix)) { final String scopeSelector = "override." + prefix; //$NON-NLS-1$ // If it's color and getting set to null, then it probably means that user // chose to restore defaults. Does that mean we should remove override? if (event.getNewValue() == null && event.getKey().endsWith("Color")) //$NON-NLS-1$ { // Do we need to run this in a delayed job to avoid clashes when the other pref // changes come through at same time...? Job job = new UIJob("Restoring overrides of Annotation") //$NON-NLS-1$ { @Override public IStatus runInUIThread(IProgressMonitor monitor) { ThemeRule rule = getCurrentTheme() .getRuleForSelector(new ScopeSelector(scopeSelector)); if (rule != null) { getCurrentTheme().remove(rule); } return Status.OK_STATUS; } }; EclipseUtil.setSystemForJob(job); job.setPriority(Job.DECORATE); job.schedule(); } else { if (!getCurrentTheme().hasEntry(scopeSelector)) { // Store that the user has overridden this annotation in this theme int index = getCurrentTheme().getTokens().size(); getCurrentTheme().addNewRule(index, "Annotation Override - " + prefix, //$NON-NLS-1$ new ScopeSelector(scopeSelector), null); } } break; } } } }); } public synchronized static ThemeManager instance() { if (fgInstance == null) { fgInstance = new ThemeManager(); } return fgInstance; } private TextAttribute getTextAttribute(String name) { if (getCurrentTheme() != null) { return getCurrentTheme().getTextAttribute(name); } return new TextAttribute(ThemePlugin.getDefault().getColorManager().getColor(new RGB(255, 255, 255))); } /** * Lazily init the current theme. */ public Theme getCurrentTheme() { if (fCurrentTheme == null) { synchronized (this) { String activeThemeName = Platform.getPreferencesService().getString(ThemePlugin.PLUGIN_ID, IPreferenceConstants.ACTIVE_THEME, ThemerPreferenceInitializer.DEFAULT_THEME, null); if (activeThemeName != null) { fCurrentTheme = getTheme(activeThemeName); } if (fCurrentTheme == null) { // if we can't find the default theme, just use the first one in the list if (!getThemeNames().isEmpty()) { fCurrentTheme = getTheme(getThemeNames().iterator().next()); } } if (fCurrentTheme != null) { setCurrentTheme(fCurrentTheme); } } } return fCurrentTheme; } /** * Set the new theme to use, this involves setting prefs across a number of plugins. */ public void setCurrentTheme(Theme theme) { fCurrentTheme = theme; // Set the find in file search color setSearchResultColor(theme); // Set the color for the search result annotation, the pref key is "searchResultIndicationColor" setAnnotationColorsToMatchTheme(theme); // Also set the standard eclipse editor props, like fg, bg, selection fg, bg setAptanaEditorColorsToMatchTheme(theme); // Set the diff/compare colors based on theme setCompareColors("com.aptana.editor.common", true); //$NON-NLS-1$ setCompareColors("org.eclipse.ui.editors", ThemePlugin.applyToAllEditors()); //$NON-NLS-1$ // We notify in UI-thread because of APSTUD-7392 // (in practice this almost always happens in the UI thread anyways, but it's // possible that at some circumstance this happens from a background thread). UIUtils.runInUIThread(new Runnable() { public void run() { notifyThemeChangeListeners(fCurrentTheme); } }); forceFontsUpToDate(); } // APSTUD-4152 private void setCompareColors(String nodeName, boolean override) { IEclipsePreferences instancePrefs = EclipseUtil.instanceScope().getNode(nodeName); if (override) { RGB bg = getCurrentTheme().getBackground(); RGB inverted = new RGB(255 - bg.red, 255 - bg.green, 255 - bg.blue); JFaceResources.getColorRegistry().put("INCOMING_COLOR", inverted); //$NON-NLS-1$ JFaceResources.getColorRegistry().put("OUTGOING_COLOR", inverted); //$NON-NLS-1$ instancePrefs.put("INCOMING_COLOR", StringConverter.asString(inverted)); //$NON-NLS-1$ instancePrefs.put("OUTGOING_COLOR", StringConverter.asString(inverted)); //$NON-NLS-1$ } else { // Revert to defaults if we have them IEclipsePreferences defPrefs = EclipseUtil.defaultScope().getNode(nodeName); String value = defPrefs.get("OUTGOING_COLOR", null); //$NON-NLS-1$ if (value != null) { try { RGB rgb = StringConverter.asRGB(value); if (rgb != null) { JFaceResources.getColorRegistry().put("OUTGOING_COLOR", rgb); //$NON-NLS-1$ } } catch (DataFormatException e) { // ignore } } value = defPrefs.get("INCOMING_COLOR", null); //$NON-NLS-1$ if (value != null) { try { RGB rgb = StringConverter.asRGB(value); if (rgb != null) { JFaceResources.getColorRegistry().put("INCOMING_COLOR", rgb); //$NON-NLS-1$ } } catch (DataFormatException e) { // ignore } } // Now remove the instance prefs instancePrefs.remove("INCOMING_COLOR"); //$NON-NLS-1$ instancePrefs.remove("OUTGOING_COLOR"); //$NON-NLS-1$ } try { instancePrefs.flush(); } catch (BackingStoreException e) { IdeLog.logError(ThemePlugin.getDefault(), e); } } private void setSearchResultColor(Theme theme) { IEclipsePreferences prefs = EclipseUtil.instanceScope().getNode("org.eclipse.search"); //$NON-NLS-1$ prefs.put("org.eclipse.search.potentialMatch.fgColor", toString(theme.getSearchResultColor())); //$NON-NLS-1$ try { prefs.flush(); } catch (BackingStoreException e) { IdeLog.logError(ThemePlugin.getDefault(), e); } } private void forceFontsUpToDate() { final String[] fontIds = new String[] { IThemeManager.VIEW_FONT_NAME, JFaceResources.TEXT_FONT, "org.eclipse.ui.workbench.texteditor.blockSelectionModeFont" }; //$NON-NLS-1$ UIUtils.getDisplay().asyncExec(new Runnable() { public void run() { for (String fontId : fontIds) { Font fFont = JFaceResources.getFontRegistry().get(fontId); // Only set new values if they're different from existing! Font existing = JFaceResources.getFont(fontId); String existingString = StringUtil.EMPTY; if (!existing.isDisposed()) { existingString = PreferenceConverter.getStoredRepresentation(existing.getFontData()); } String fdString = PreferenceConverter.getStoredRepresentation(fFont.getFontData()); if (!existingString.equals(fdString)) { // put in registry... JFaceResources.getFontRegistry().put(fontId, fFont.getFontData()); } } } }); } /** * Set specific pref values that we use to listen for when the theme has changed across our plugins. This ignals to * them the theme has been changed and they need to update their settings to match. * * @param theme */ private void notifyThemeChangeListeners(Theme theme) { IEclipsePreferences prefs = EclipseUtil.instanceScope().getNode(ThemePlugin.PLUGIN_ID); prefs.put(IPreferenceConstants.ACTIVE_THEME, theme.getName()); prefs.putLong(THEME_CHANGED, System.currentTimeMillis()); try { prefs.flush(); } catch (BackingStoreException e) { IdeLog.logError(ThemePlugin.getDefault(), e); } } /** * Set the FG, BG, selection and current line colors on our editors. * * @param theme */ private void setAptanaEditorColorsToMatchTheme(Theme theme) { IEclipsePreferences prefs = EclipseUtil.instanceScope().getNode("com.aptana.editor.common"); //$NON-NLS-1$ prefs.putBoolean(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT, false); prefs.put(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND, toString(theme.getForeground())); prefs.putBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT, false); prefs.put(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND, toString(theme.getBackground())); prefs.putBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT, false); prefs.put(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND, toString(theme.getForeground())); prefs.put(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR, toString(theme.getLineHighlightAgainstBG())); try { prefs.flush(); } catch (BackingStoreException e) { IdeLog.logError(ThemePlugin.getDefault(), e); } } private void setAnnotationColorsToMatchTheme(Theme theme) { IEclipsePreferences prefs = EclipseUtil.instanceScope().getNode("org.eclipse.ui.editors"); //$NON-NLS-1$ if (!theme.hasEntry("override.searchResultIndication")) //$NON-NLS-1$ { prefs.put("searchResultIndicationColor", toString(theme.getSearchResultColor())); //$NON-NLS-1$ } // TODO Use markup.changed bg color for "decoration color" in Prefs>General>Appearance>Colors and Fonts // TODO Move this stuff over to theme change listeners in the XML/HTML/Ruby editor plugins? if (!theme.hasEntry("override.xmlTagPairOccurrenceIndication")) //$NON-NLS-1$ { prefs.putBoolean("xmlTagPairOccurrenceIndicationHighlighting", false); //$NON-NLS-1$ prefs.putBoolean("xmlTagPairOccurrenceIndication", true); //$NON-NLS-1$ prefs.put("xmlTagPairOccurrenceIndicationColor", toString(theme.getOccurenceHighlightColor())); //$NON-NLS-1$ prefs.put("xmlTagPairOccurrenceIndicationTextStyle", AnnotationPreference.STYLE_BOX); //$NON-NLS-1$ } if (!theme.hasEntry("override.htmlTagPairOccurrenceIndication")) //$NON-NLS-1$ { prefs.putBoolean("htmlTagPairOccurrenceIndicationHighlighting", false); //$NON-NLS-1$ prefs.putBoolean("htmlTagPairOccurrenceIndication", true); //$NON-NLS-1$ prefs.put("htmlTagPairOccurrenceIndicationColor", toString(theme.getOccurenceHighlightColor())); //$NON-NLS-1$ prefs.put("htmlTagPairOccurrenceIndicationTextStyle", AnnotationPreference.STYLE_BOX); //$NON-NLS-1$ } if (!theme.hasEntry("override.rubyBlockPairOccurrenceIndication")) //$NON-NLS-1$ { prefs.putBoolean("rubyBlockPairOccurrenceIndicationHighlighting", false); //$NON-NLS-1$ prefs.putBoolean("rubyBlockPairOccurrenceIndication", true); //$NON-NLS-1$ prefs.put("rubyBlockPairOccurrenceIndicationColor", toString(theme.getOccurenceHighlightColor())); //$NON-NLS-1$ prefs.put("rubyBlockPairOccurrenceIndicationTextStyle", AnnotationPreference.STYLE_BOX); //$NON-NLS-1$ } // PyDev Occurrences (com.python.pydev.occurrences) // Override them if pydev is set to use our themes if (Platform.getPreferencesService().getBoolean("org.python.pydev.red_core", "PYDEV_USE_APTANA_THEMES", //$NON-NLS-1$//$NON-NLS-2$ true, null)) { if (!theme.hasEntry("override.pydevOccurrenceIndication")) //$NON-NLS-1$ { MarkerAnnotationPreferences preferences = new MarkerAnnotationPreferences(); AnnotationPreference pydevOccurPref = null; for (Object obj : preferences.getAnnotationPreferences()) { AnnotationPreference pref = (AnnotationPreference) obj; Object type = pref.getAnnotationType(); if ("com.python.pydev.occurrences".equals(type)) //$NON-NLS-1$ { pydevOccurPref = pref; } } if (pydevOccurPref != null) { if (pydevOccurPref.getTextStylePreferenceKey() != null) { // Now that pydev supports text style, use the box style and don't highlight. prefs.putBoolean("pydevOccurrenceHighlighting", false); //$NON-NLS-1$ prefs.putBoolean("pydevOccurrenceIndication", true); //$NON-NLS-1$ prefs.put("pydevOccurrenceIndicationColor", toString(theme.getOccurenceHighlightColor())); //$NON-NLS-1$ prefs.put("pydevOccurrenceIndicationTextStyle", AnnotationPreference.STYLE_BOX); //$NON-NLS-1$ } else { // Must use highlighting, since we're against older pydev that had no text style prefs.putBoolean("pydevOccurrenceHighlighting", true); //$NON-NLS-1$ prefs.putBoolean("pydevOccurrenceIndication", true); //$NON-NLS-1$ prefs.put("pydevOccurrenceIndicationColor", toString(theme.getSearchResultColor())); //$NON-NLS-1$ } } } } try { prefs.flush(); } catch (BackingStoreException e) { IdeLog.logError(ThemePlugin.getDefault(), e); } } private static String toString(RGB selection) { return StringConverter.asString(selection); } /** * Attempts to find the theme with a given name, first from prefs, then from pre-packaged builtins. Will return null * if no match is found. */ public Theme getTheme(String name) { // Try to see if we have a copy in prefs as a user theme Theme loaded = null; try { loaded = loadUserTheme(name); } catch (Exception e) { IdeLog.logError(ThemePlugin.getDefault(), MessageFormat.format("Failed to load theme {0} from preferences.", name), e); //$NON-NLS-1$ } if (loaded != null) { return loaded; } // Ok, no user theme by that name, load up the builtins. Loading them once should save a copy to prefs (user) // for future... try { return loadBuiltinTheme(name); } catch (Exception e) { IdeLog.logError(ThemePlugin.getDefault(), MessageFormat.format("Failed to load theme {0} from builtins.", name), e); //$NON-NLS-1$ } return null; } /** * laziliy init the set of theme names. */ public synchronized Set<String> getThemeNames() { if (fThemeNames == null) { fThemeNames = new HashSet<String>(); // Add names of themes from builtins... fThemeNames.addAll(getBuiltinThemeNames()); // Look in prefs to see what user themes are stored there, garb their names IScopeContext[] scopes = new IScopeContext[] { EclipseUtil.instanceScope(), EclipseUtil.defaultScope() }; for (IScopeContext scope : scopes) { IEclipsePreferences prefs = scope.getNode(ThemePlugin.PLUGIN_ID); Preferences preferences = prefs.node(ThemeManager.THEMES_NODE); try { String[] themeNames = preferences.keys(); fThemeNames.addAll(Arrays.asList(themeNames)); } catch (BackingStoreException e) { IdeLog.logError(ThemePlugin.getDefault(), e); } } } return fThemeNames; } private Theme loadUserTheme(String themeName) { InputStream byteStream = null; try { byte[] array = Platform.getPreferencesService().getByteArray(ThemePlugin.PLUGIN_ID, THEMES_NODE + "/" + themeName, null, null); //$NON-NLS-1$ if (array == null) { return null; } byteStream = new ByteArrayInputStream(array); Properties props = new OrderedProperties(); props.load(byteStream); // if it looks like the byte array was not Base64 decoded, try decoding and then running it through if (!props.containsKey(Theme.THEME_NAME_PROP_KEY)) // anything else we can check for this? { IdeLog.logWarning(ThemePlugin.getDefault(), MessageFormat.format( "User theme {0} de-serialized, but was left Base64 encoded. Manually decoding and trying to load.", //$NON-NLS-1$ themeName)); byteStream = new ByteArrayInputStream(Base64.decode(array)); props = new OrderedProperties(); props.load(byteStream); } return new Theme(ThemePlugin.getDefault().getColorManager(), props); } catch (IllegalArgumentException iae) { // Fallback to load theme that was saved in prefs as XML string String xml = Platform.getPreferencesService().getString(ThemePlugin.PLUGIN_ID, THEMES_NODE + "/" + themeName, null, null); //$NON-NLS-1$ if (xml != null) { InputStream stream = null; try { stream = new ByteArrayInputStream(xml.getBytes(IOUtil.UTF_8)); Properties props = new OrderedProperties(); props.loadFromXML(stream); // Now store it as byte array explicitly so we don't run into this! Theme theme = new Theme(ThemePlugin.getDefault().getColorManager(), props); theme.save(); return theme; } catch (Exception e) { IdeLog.logError(ThemePlugin.getDefault(), e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // ignore } } } } } catch (IOException e) { IdeLog.logError(ThemePlugin.getDefault(), e); } finally { if (byteStream != null) { try { byteStream.close(); } catch (IOException e) { // ignore } } } return null; } private OrderedProperties getBuiltinThemeProperties(String themeName) { Collection<URL> urls = getBuiltinThemeURLs(); if (CollectionsUtil.isEmpty(urls)) { return null; } for (URL url : urls) { try { // Try forcing the file to be extracted out from zip before we try to read it InputStream stream = FileLocator.toFileURL(url).openStream(); try { OrderedProperties props = new OrderedProperties(); props.load(stream); String loadedName = props.getProperty(Theme.THEME_NAME_PROP_KEY); if (!themeName.equals(loadedName)) { continue; } String multipleThemeExtends = props.getProperty(Theme.THEME_EXTENDS_PROP_KEY); // If we extend one or more other themes, recursively load their properties... if (multipleThemeExtends != null) { OrderedProperties newProperties = new OrderedProperties(); String[] pieces = multipleThemeExtends.split(","); //$NON-NLS-1$ for (String themeExtends : pieces) { Properties extended = getBuiltinThemeProperties(themeExtends); if (extended == null) { throw new IllegalStateException(MessageFormat .format(Messages.ThemeManager_ERR_NoThemeFound, themeExtends, loadedName)); } newProperties.putAll(extended); } newProperties.putAll(props); // We don't want the final extends props in the properties. newProperties.remove(Theme.THEME_EXTENDS_PROP_KEY); // Sanity check Assert.isTrue(newProperties.get(Theme.THEME_NAME_PROP_KEY).equals(themeName)); return newProperties; } return props; } finally { try { stream.close(); } catch (IOException e) { // ignore } } } catch (Exception e) { IdeLog.logError(ThemePlugin.getDefault(), url.toString(), e); } } return null; } private synchronized Set<String> getBuiltinThemeNames() { if (fBuiltins == null) { fBuiltins = new HashSet<String>(); Collection<URL> urls = getBuiltinThemeURLs(); if (urls == null || urls.isEmpty()) { return fBuiltins; } for (URL url : urls) { InputStream stream = null; try { // Try forcing the file to be extracted out from zip before we try to read it stream = FileLocator.toFileURL(url).openStream(); OrderedProperties props = new OrderedProperties(); props.load(stream); String loadedName = props.getProperty(Theme.THEME_NAME_PROP_KEY); // Don't include the abstract themes in the list, they're meant just for extending if (loadedName != null && !loadedName.startsWith("abstract_theme")) //$NON-NLS-1$ { fBuiltins.add(loadedName); } } catch (Exception e) { IdeLog.logError(ThemePlugin.getDefault(), e); } finally { try { if (stream != null) { stream.close(); } } catch (IOException e) { // ignore } } } } return fBuiltins; } private Collection<URL> getBuiltinThemeURLs() { ThemePlugin themePlugin = ThemePlugin.getDefault(); if (themePlugin == null) { return Collections.emptyList(); } Bundle bundle = themePlugin.getBundle(); if (bundle == null) { return Collections.emptyList(); } ArrayList<URL> collection = new ArrayList<URL>(); Enumeration<URL> enumeration = bundle.findEntries("themes", "*.properties", false); //$NON-NLS-1$ //$NON-NLS-2$ while (enumeration.hasMoreElements()) { collection.add(enumeration.nextElement()); } collection.trimToSize(); return collection; } public Theme loadBuiltinTheme(String themeName) { OrderedProperties properties = getBuiltinThemeProperties(themeName); if (properties == null) { return null; } return loadBuiltinTheme(properties); } private Theme loadBuiltinTheme(Properties props) { try { return new Theme(ThemePlugin.getDefault().getColorManager(), props); } catch (Exception e) { IdeLog.logError(ThemePlugin.getDefault(), e); } return null; } public IToken getToken(String scope) { return new Token(getTextAttribute(scope)); } public void addTheme(Theme newTheme) { newTheme.save(); getThemeNames().add(newTheme.getName()); } public void removeTheme(Theme theme) { Theme activeTheme = getCurrentTheme(); getThemeNames().remove(theme.getName()); // change active theme if we just removed it if (activeTheme.getName().equals(theme.getName())) { // load first theme from list of names setCurrentTheme(getTheme(getThemeNames().iterator().next())); } } public boolean isBuiltinTheme(String themeName) { return getBuiltinThemeNames().contains(themeName); } public IStatus validateThemeName(String name) { if (StringUtil.isEmpty(name)) { return new Status(IStatus.ERROR, ThemePlugin.PLUGIN_ID, Messages.ThemeManager_NameNonEmptyMsg); } if (getThemeNames().contains(name.trim())) { return new Status(IStatus.ERROR, ThemePlugin.PLUGIN_ID, Messages.ThemeManager_NameAlreadyExistsMsg); } return Status.OK_STATUS; } }