Mercurial > projects > dwt-addons
diff dwtx/jface/text/templates/persistence/TemplateStore.d @ 129:eb30df5ca28b
Added JFace Text sources
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 23 Aug 2008 19:10:48 +0200 |
parents | |
children | c4fb132a086c |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/text/templates/persistence/TemplateStore.d Sat Aug 23 19:10:48 2008 +0200 @@ -0,0 +1,466 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.text.templates.persistence.TemplateStore; + +import dwt.dwthelper.utils; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import dwtx.core.runtime.Assert; +import dwtx.jface.preference.IPersistentPreferenceStore; +import dwtx.jface.preference.IPreferenceStore; +import dwtx.jface.text.templates.ContextTypeRegistry; +import dwtx.jface.text.templates.Template; +import dwtx.jface.text.templates.TemplateException; +import dwtx.jface.util.IPropertyChangeListener; +import dwtx.jface.util.PropertyChangeEvent; + +/** + * A collection of templates. Clients may instantiate this class. In order to + * load templates contributed using the <code>dwtx.ui.editors.templates</code> + * extension point, use a <code>ContributionTemplateStore</code>. + * + * @since 3.0 + */ +public class TemplateStore { + /** The stored templates. */ + private final List fTemplates= new ArrayList(); + /** The preference store. */ + private IPreferenceStore fPreferenceStore; + /** + * The key into <code>fPreferenceStore</code> the value of which holds custom templates + * encoded as XML. + */ + private String fKey; + /** + * The context type registry, or <code>null</code> if all templates regardless + * of context type should be loaded. + */ + private ContextTypeRegistry fRegistry; + /** + * Set to <code>true</code> if property change events should be ignored (e.g. during writing + * to the preference store). + * + * @since 3.2 + */ + private bool fIgnorePreferenceStoreChanges= false; + /** + * The property listener, if any is registered, <code>null</code> otherwise. + * + * @since 3.2 + */ + private IPropertyChangeListener fPropertyListener; + + + /** + * Creates a new template store. + * + * @param store the preference store in which to store custom templates + * under <code>key</code> + * @param key the key into <code>store</code> where to store custom + * templates + */ + public TemplateStore(IPreferenceStore store, String key) { + Assert.isNotNull(store); + Assert.isNotNull(key); + fPreferenceStore= store; + fKey= key; + } + + /** + * Creates a new template store with a context type registry. Only templates + * that specify a context type contained in the registry will be loaded by + * this store if the registry is not <code>null</code>. + * + * @param registry a context type registry, or <code>null</code> if all + * templates should be loaded + * @param store the preference store in which to store custom templates + * under <code>key</code> + * @param key the key into <code>store</code> where to store custom + * templates + */ + public TemplateStore(ContextTypeRegistry registry, IPreferenceStore store, String key) { + this(store, key); + fRegistry= registry; + } + + /** + * Loads the templates from contributions and preferences. + * + * @throws IOException if loading fails. + */ + public void load() throws IOException { + fTemplates.clear(); + loadContributedTemplates(); + loadCustomTemplates(); + } + + /** + * Starts listening for property changes on the preference store. If the configured preference + * key changes, the template store is {@link #load() reloaded}. Call + * {@link #stopListeningForPreferenceChanges()} to remove any listener and stop the + * auto-updating behavior. + * + * @since 3.2 + */ + public final void startListeningForPreferenceChanges() { + if (fPropertyListener is null) { + fPropertyListener= new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + /* + * Don't load if we are in the process of saving ourselves. We are in sync anyway after the + * save operation, and clients may trigger reloading by listening to preference store + * updates. + */ + if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getProperty())) + try { + load(); + } catch (IOException x) { + handleException(x); + } + } + }; + fPreferenceStore.addPropertyChangeListener(fPropertyListener); + } + + } + + /** + * Stops the auto-updating behavior started by calling + * {@link #startListeningForPreferenceChanges()}. + * + * @since 3.2 + */ + public final void stopListeningForPreferenceChanges() { + if (fPropertyListener !is null) { + fPreferenceStore.removePropertyChangeListener(fPropertyListener); + fPropertyListener= null; + } + } + + /** + * Handles an {@link IOException} thrown during reloading the preferences due to a preference + * store update. The default is to write to stderr. + * + * @param x the exception + * @since 3.2 + */ + protected void handleException(IOException x) { + x.printStackTrace(); + } + + /** + * Hook method to load contributed templates. Contributed templates are superseded + * by customized versions of user added templates stored in the preferences. + * <p> + * The default implementation does nothing.</p> + * + * @throws IOException if loading fails + */ + protected void loadContributedTemplates() throws IOException { + } + + /** + * Adds a template to the internal store. The added templates must have + * a unique id. + * + * @param data the template data to add + */ + protected void internalAdd(TemplatePersistenceData data) { + if (!data.isCustom()) { + // check if the added template is not a duplicate id + String id= data.getId(); + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData d2= (TemplatePersistenceData) it.next(); + if (d2.getId() !is null && d2.getId().equals(id)) + return; + } + fTemplates.add(data); + } + } + + /** + * Saves the templates to the preferences. + * + * @throws IOException if the templates cannot be written + */ + public void save() throws IOException { + ArrayList custom= new ArrayList(); + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData data= (TemplatePersistenceData) it.next(); + if (data.isCustom() && !(data.isUserAdded() && data.isDeleted())) // don't save deleted user-added templates + custom.add(data); + } + + StringWriter output= new StringWriter(); + TemplateReaderWriter writer= new TemplateReaderWriter(); + writer.save((TemplatePersistenceData[]) custom.toArray(new TemplatePersistenceData[custom.size()]), output); + + fIgnorePreferenceStoreChanges= true; + try { + fPreferenceStore.setValue(fKey, output.toString()); + if (fPreferenceStore instanceof IPersistentPreferenceStore) + ((IPersistentPreferenceStore)fPreferenceStore).save(); + } finally { + fIgnorePreferenceStoreChanges= false; + } + } + + /** + * Adds a template encapsulated in its persistent form. + * + * @param data the template to add + */ + public void add(TemplatePersistenceData data) { + + if (!validateTemplate(data.getTemplate())) + return; + + if (data.isUserAdded()) { + fTemplates.add(data); + } else { + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData d2= (TemplatePersistenceData) it.next(); + if (d2.getId() !is null && d2.getId().equals(data.getId())) { + d2.setTemplate(data.getTemplate()); + d2.setDeleted(data.isDeleted()); + d2.setEnabled(data.isEnabled()); + return; + } + } + + // add an id which is not contributed as add-on + if (data.getTemplate() !is null) { + TemplatePersistenceData newData= new TemplatePersistenceData(data.getTemplate(), data.isEnabled()); + fTemplates.add(newData); + } + } + } + + /** + * Removes a template from the store. + * + * @param data the template to remove + */ + public void delete(TemplatePersistenceData data) { + if (data.isUserAdded()) + fTemplates.remove(data); + else + data.setDeleted(true); + } + + /** + * Restores all contributed templates that have been deleted. + */ + public void restoreDeleted() { + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData data= (TemplatePersistenceData) it.next(); + if (data.isDeleted()) + data.setDeleted(false); + } + } + + /** + * Deletes all user-added templates and reverts all contributed templates. + */ + public void restoreDefaults() { + try { + fIgnorePreferenceStoreChanges= true; + fPreferenceStore.setToDefault(fKey); + } finally { + fIgnorePreferenceStoreChanges= false; + } + try { + load(); + } catch (IOException x) { + // can't log from jface-text + x.printStackTrace(); + } + } + + /** + * Returns all enabled templates. + * + * @return all enabled templates + */ + public Template[] getTemplates() { + return getTemplates(null); + } + + /** + * Returns all enabled templates for the given context type. + * + * @param contextTypeId the id of the context type of the requested templates, or <code>null</code> if all templates should be returned + * @return all enabled templates for the given context type + */ + public Template[] getTemplates(String contextTypeId) { + List templates= new ArrayList(); + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData data= (TemplatePersistenceData) it.next(); + if (data.isEnabled() && !data.isDeleted() && (contextTypeId is null || contextTypeId.equals(data.getTemplate().getContextTypeId()))) + templates.add(data.getTemplate()); + } + + return (Template[]) templates.toArray(new Template[templates.size()]); + } + + /** + * Returns the first enabled template that matches the name. + * + * @param name the name of the template searched for + * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found + */ + public Template findTemplate(String name) { + return findTemplate(name, null); + } + + /** + * Returns the first enabled template that matches both name and context type id. + * + * @param name the name of the template searched for + * @param contextTypeId the context type id to clip unwanted templates, or <code>null</code> if any context type is OK + * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found + */ + public Template findTemplate(String name, String contextTypeId) { + Assert.isNotNull(name); + + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData data= (TemplatePersistenceData) it.next(); + Template template= data.getTemplate(); + if (data.isEnabled() && !data.isDeleted() + && (contextTypeId is null || contextTypeId.equals(template.getContextTypeId())) + && name.equals(template.getName())) + return template; + } + + return null; + } + + /** + * Returns the first enabled template that matches the given template id. + * + * @param id the id of the template searched for + * @return the first enabled template that matches id, or <code>null</code> if none is found + * @since 3.1 + */ + public Template findTemplateById(String id) { + TemplatePersistenceData data= getTemplateData(id); + if (data !is null && !data.isDeleted()) + return data.getTemplate(); + + return null; + } + + /** + * Returns all template data. + * + * @param includeDeleted whether to include deleted data + * @return all template data, whether enabled or not + */ + public TemplatePersistenceData[] getTemplateData(bool includeDeleted) { + List datas= new ArrayList(); + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData data= (TemplatePersistenceData) it.next(); + if (includeDeleted || !data.isDeleted()) + datas.add(data); + } + + return (TemplatePersistenceData[]) datas.toArray(new TemplatePersistenceData[datas.size()]); + } + + /** + * Returns the template data of the template with id <code>id</code> or + * <code>null</code> if no such template can be found. + * + * @param id the id of the template data + * @return the template data of the template with id <code>id</code> or <code>null</code> + * @since 3.1 + */ + public TemplatePersistenceData getTemplateData(String id) { + Assert.isNotNull(id); + for (Iterator it= fTemplates.iterator(); it.hasNext();) { + TemplatePersistenceData data= (TemplatePersistenceData) it.next(); + if (id.equals(data.getId())) + return data; + } + + return null; + } + + private void loadCustomTemplates() throws IOException { + String pref= fPreferenceStore.getString(fKey); + if (pref !is null && pref.trim().length() > 0) { + Reader input= new StringReader(pref); + TemplateReaderWriter reader= new TemplateReaderWriter(); + TemplatePersistenceData[] datas= reader.read(input); + for (int i= 0; i < datas.length; i++) { + TemplatePersistenceData data= datas[i]; + add(data); + } + } + } + + /** + * Validates a template against the context type registered in the context + * type registry. Returns always <code>true</code> if no registry is + * present. + * + * @param template the template to validate + * @return <code>true</code> if validation is successful or no context + * type registry is specified, <code>false</code> if validation + * fails + */ + private bool validateTemplate(Template template) { + String contextTypeId= template.getContextTypeId(); + if (contextExists(contextTypeId)) { + if (fRegistry !is null) + try { + fRegistry.getContextType(contextTypeId).validate(template.getPattern()); + } catch (TemplateException e) { + return false; + } + return true; + } + + return false; + } + + /** + * Returns <code>true</code> if a context type id specifies a valid context type + * or if no context type registry is present. + * + * @param contextTypeId the context type id to look for + * @return <code>true</code> if the context type specified by the id + * is present in the context type registry, or if no registry is + * specified + */ + private bool contextExists(String contextTypeId) { + return contextTypeId !is null && (fRegistry is null || fRegistry.getContextType(contextTypeId) !is null); + } + + /** + * Returns the registry. + * + * @return Returns the registry + */ + protected final ContextTypeRegistry getRegistry() { + return fRegistry; + } +} +