comparison 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
comparison
equal deleted inserted replaced
128:8df1d4193877 129:eb30df5ca28b
1 /*******************************************************************************
2 * Copyright (c) 2000, 2006 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.jface.text.templates.persistence.TemplateStore;
14
15 import dwt.dwthelper.utils;
16
17 import java.io.IOException;
18 import java.io.Reader;
19 import java.io.StringReader;
20 import java.io.StringWriter;
21 import java.util.ArrayList;
22 import java.util.Iterator;
23 import java.util.List;
24
25 import dwtx.core.runtime.Assert;
26 import dwtx.jface.preference.IPersistentPreferenceStore;
27 import dwtx.jface.preference.IPreferenceStore;
28 import dwtx.jface.text.templates.ContextTypeRegistry;
29 import dwtx.jface.text.templates.Template;
30 import dwtx.jface.text.templates.TemplateException;
31 import dwtx.jface.util.IPropertyChangeListener;
32 import dwtx.jface.util.PropertyChangeEvent;
33
34 /**
35 * A collection of templates. Clients may instantiate this class. In order to
36 * load templates contributed using the <code>dwtx.ui.editors.templates</code>
37 * extension point, use a <code>ContributionTemplateStore</code>.
38 *
39 * @since 3.0
40 */
41 public class TemplateStore {
42 /** The stored templates. */
43 private final List fTemplates= new ArrayList();
44 /** The preference store. */
45 private IPreferenceStore fPreferenceStore;
46 /**
47 * The key into <code>fPreferenceStore</code> the value of which holds custom templates
48 * encoded as XML.
49 */
50 private String fKey;
51 /**
52 * The context type registry, or <code>null</code> if all templates regardless
53 * of context type should be loaded.
54 */
55 private ContextTypeRegistry fRegistry;
56 /**
57 * Set to <code>true</code> if property change events should be ignored (e.g. during writing
58 * to the preference store).
59 *
60 * @since 3.2
61 */
62 private bool fIgnorePreferenceStoreChanges= false;
63 /**
64 * The property listener, if any is registered, <code>null</code> otherwise.
65 *
66 * @since 3.2
67 */
68 private IPropertyChangeListener fPropertyListener;
69
70
71 /**
72 * Creates a new template store.
73 *
74 * @param store the preference store in which to store custom templates
75 * under <code>key</code>
76 * @param key the key into <code>store</code> where to store custom
77 * templates
78 */
79 public TemplateStore(IPreferenceStore store, String key) {
80 Assert.isNotNull(store);
81 Assert.isNotNull(key);
82 fPreferenceStore= store;
83 fKey= key;
84 }
85
86 /**
87 * Creates a new template store with a context type registry. Only templates
88 * that specify a context type contained in the registry will be loaded by
89 * this store if the registry is not <code>null</code>.
90 *
91 * @param registry a context type registry, or <code>null</code> if all
92 * templates should be loaded
93 * @param store the preference store in which to store custom templates
94 * under <code>key</code>
95 * @param key the key into <code>store</code> where to store custom
96 * templates
97 */
98 public TemplateStore(ContextTypeRegistry registry, IPreferenceStore store, String key) {
99 this(store, key);
100 fRegistry= registry;
101 }
102
103 /**
104 * Loads the templates from contributions and preferences.
105 *
106 * @throws IOException if loading fails.
107 */
108 public void load() throws IOException {
109 fTemplates.clear();
110 loadContributedTemplates();
111 loadCustomTemplates();
112 }
113
114 /**
115 * Starts listening for property changes on the preference store. If the configured preference
116 * key changes, the template store is {@link #load() reloaded}. Call
117 * {@link #stopListeningForPreferenceChanges()} to remove any listener and stop the
118 * auto-updating behavior.
119 *
120 * @since 3.2
121 */
122 public final void startListeningForPreferenceChanges() {
123 if (fPropertyListener is null) {
124 fPropertyListener= new IPropertyChangeListener() {
125 public void propertyChange(PropertyChangeEvent event) {
126 /*
127 * Don't load if we are in the process of saving ourselves. We are in sync anyway after the
128 * save operation, and clients may trigger reloading by listening to preference store
129 * updates.
130 */
131 if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getProperty()))
132 try {
133 load();
134 } catch (IOException x) {
135 handleException(x);
136 }
137 }
138 };
139 fPreferenceStore.addPropertyChangeListener(fPropertyListener);
140 }
141
142 }
143
144 /**
145 * Stops the auto-updating behavior started by calling
146 * {@link #startListeningForPreferenceChanges()}.
147 *
148 * @since 3.2
149 */
150 public final void stopListeningForPreferenceChanges() {
151 if (fPropertyListener !is null) {
152 fPreferenceStore.removePropertyChangeListener(fPropertyListener);
153 fPropertyListener= null;
154 }
155 }
156
157 /**
158 * Handles an {@link IOException} thrown during reloading the preferences due to a preference
159 * store update. The default is to write to stderr.
160 *
161 * @param x the exception
162 * @since 3.2
163 */
164 protected void handleException(IOException x) {
165 x.printStackTrace();
166 }
167
168 /**
169 * Hook method to load contributed templates. Contributed templates are superseded
170 * by customized versions of user added templates stored in the preferences.
171 * <p>
172 * The default implementation does nothing.</p>
173 *
174 * @throws IOException if loading fails
175 */
176 protected void loadContributedTemplates() throws IOException {
177 }
178
179 /**
180 * Adds a template to the internal store. The added templates must have
181 * a unique id.
182 *
183 * @param data the template data to add
184 */
185 protected void internalAdd(TemplatePersistenceData data) {
186 if (!data.isCustom()) {
187 // check if the added template is not a duplicate id
188 String id= data.getId();
189 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
190 TemplatePersistenceData d2= (TemplatePersistenceData) it.next();
191 if (d2.getId() !is null && d2.getId().equals(id))
192 return;
193 }
194 fTemplates.add(data);
195 }
196 }
197
198 /**
199 * Saves the templates to the preferences.
200 *
201 * @throws IOException if the templates cannot be written
202 */
203 public void save() throws IOException {
204 ArrayList custom= new ArrayList();
205 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
206 TemplatePersistenceData data= (TemplatePersistenceData) it.next();
207 if (data.isCustom() && !(data.isUserAdded() && data.isDeleted())) // don't save deleted user-added templates
208 custom.add(data);
209 }
210
211 StringWriter output= new StringWriter();
212 TemplateReaderWriter writer= new TemplateReaderWriter();
213 writer.save((TemplatePersistenceData[]) custom.toArray(new TemplatePersistenceData[custom.size()]), output);
214
215 fIgnorePreferenceStoreChanges= true;
216 try {
217 fPreferenceStore.setValue(fKey, output.toString());
218 if (fPreferenceStore instanceof IPersistentPreferenceStore)
219 ((IPersistentPreferenceStore)fPreferenceStore).save();
220 } finally {
221 fIgnorePreferenceStoreChanges= false;
222 }
223 }
224
225 /**
226 * Adds a template encapsulated in its persistent form.
227 *
228 * @param data the template to add
229 */
230 public void add(TemplatePersistenceData data) {
231
232 if (!validateTemplate(data.getTemplate()))
233 return;
234
235 if (data.isUserAdded()) {
236 fTemplates.add(data);
237 } else {
238 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
239 TemplatePersistenceData d2= (TemplatePersistenceData) it.next();
240 if (d2.getId() !is null && d2.getId().equals(data.getId())) {
241 d2.setTemplate(data.getTemplate());
242 d2.setDeleted(data.isDeleted());
243 d2.setEnabled(data.isEnabled());
244 return;
245 }
246 }
247
248 // add an id which is not contributed as add-on
249 if (data.getTemplate() !is null) {
250 TemplatePersistenceData newData= new TemplatePersistenceData(data.getTemplate(), data.isEnabled());
251 fTemplates.add(newData);
252 }
253 }
254 }
255
256 /**
257 * Removes a template from the store.
258 *
259 * @param data the template to remove
260 */
261 public void delete(TemplatePersistenceData data) {
262 if (data.isUserAdded())
263 fTemplates.remove(data);
264 else
265 data.setDeleted(true);
266 }
267
268 /**
269 * Restores all contributed templates that have been deleted.
270 */
271 public void restoreDeleted() {
272 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
273 TemplatePersistenceData data= (TemplatePersistenceData) it.next();
274 if (data.isDeleted())
275 data.setDeleted(false);
276 }
277 }
278
279 /**
280 * Deletes all user-added templates and reverts all contributed templates.
281 */
282 public void restoreDefaults() {
283 try {
284 fIgnorePreferenceStoreChanges= true;
285 fPreferenceStore.setToDefault(fKey);
286 } finally {
287 fIgnorePreferenceStoreChanges= false;
288 }
289 try {
290 load();
291 } catch (IOException x) {
292 // can't log from jface-text
293 x.printStackTrace();
294 }
295 }
296
297 /**
298 * Returns all enabled templates.
299 *
300 * @return all enabled templates
301 */
302 public Template[] getTemplates() {
303 return getTemplates(null);
304 }
305
306 /**
307 * Returns all enabled templates for the given context type.
308 *
309 * @param contextTypeId the id of the context type of the requested templates, or <code>null</code> if all templates should be returned
310 * @return all enabled templates for the given context type
311 */
312 public Template[] getTemplates(String contextTypeId) {
313 List templates= new ArrayList();
314 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
315 TemplatePersistenceData data= (TemplatePersistenceData) it.next();
316 if (data.isEnabled() && !data.isDeleted() && (contextTypeId is null || contextTypeId.equals(data.getTemplate().getContextTypeId())))
317 templates.add(data.getTemplate());
318 }
319
320 return (Template[]) templates.toArray(new Template[templates.size()]);
321 }
322
323 /**
324 * Returns the first enabled template that matches the name.
325 *
326 * @param name the name of the template searched for
327 * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found
328 */
329 public Template findTemplate(String name) {
330 return findTemplate(name, null);
331 }
332
333 /**
334 * Returns the first enabled template that matches both name and context type id.
335 *
336 * @param name the name of the template searched for
337 * @param contextTypeId the context type id to clip unwanted templates, or <code>null</code> if any context type is OK
338 * @return the first enabled template that matches both name and context type id, or <code>null</code> if none is found
339 */
340 public Template findTemplate(String name, String contextTypeId) {
341 Assert.isNotNull(name);
342
343 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
344 TemplatePersistenceData data= (TemplatePersistenceData) it.next();
345 Template template= data.getTemplate();
346 if (data.isEnabled() && !data.isDeleted()
347 && (contextTypeId is null || contextTypeId.equals(template.getContextTypeId()))
348 && name.equals(template.getName()))
349 return template;
350 }
351
352 return null;
353 }
354
355 /**
356 * Returns the first enabled template that matches the given template id.
357 *
358 * @param id the id of the template searched for
359 * @return the first enabled template that matches id, or <code>null</code> if none is found
360 * @since 3.1
361 */
362 public Template findTemplateById(String id) {
363 TemplatePersistenceData data= getTemplateData(id);
364 if (data !is null && !data.isDeleted())
365 return data.getTemplate();
366
367 return null;
368 }
369
370 /**
371 * Returns all template data.
372 *
373 * @param includeDeleted whether to include deleted data
374 * @return all template data, whether enabled or not
375 */
376 public TemplatePersistenceData[] getTemplateData(bool includeDeleted) {
377 List datas= new ArrayList();
378 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
379 TemplatePersistenceData data= (TemplatePersistenceData) it.next();
380 if (includeDeleted || !data.isDeleted())
381 datas.add(data);
382 }
383
384 return (TemplatePersistenceData[]) datas.toArray(new TemplatePersistenceData[datas.size()]);
385 }
386
387 /**
388 * Returns the template data of the template with id <code>id</code> or
389 * <code>null</code> if no such template can be found.
390 *
391 * @param id the id of the template data
392 * @return the template data of the template with id <code>id</code> or <code>null</code>
393 * @since 3.1
394 */
395 public TemplatePersistenceData getTemplateData(String id) {
396 Assert.isNotNull(id);
397 for (Iterator it= fTemplates.iterator(); it.hasNext();) {
398 TemplatePersistenceData data= (TemplatePersistenceData) it.next();
399 if (id.equals(data.getId()))
400 return data;
401 }
402
403 return null;
404 }
405
406 private void loadCustomTemplates() throws IOException {
407 String pref= fPreferenceStore.getString(fKey);
408 if (pref !is null && pref.trim().length() > 0) {
409 Reader input= new StringReader(pref);
410 TemplateReaderWriter reader= new TemplateReaderWriter();
411 TemplatePersistenceData[] datas= reader.read(input);
412 for (int i= 0; i < datas.length; i++) {
413 TemplatePersistenceData data= datas[i];
414 add(data);
415 }
416 }
417 }
418
419 /**
420 * Validates a template against the context type registered in the context
421 * type registry. Returns always <code>true</code> if no registry is
422 * present.
423 *
424 * @param template the template to validate
425 * @return <code>true</code> if validation is successful or no context
426 * type registry is specified, <code>false</code> if validation
427 * fails
428 */
429 private bool validateTemplate(Template template) {
430 String contextTypeId= template.getContextTypeId();
431 if (contextExists(contextTypeId)) {
432 if (fRegistry !is null)
433 try {
434 fRegistry.getContextType(contextTypeId).validate(template.getPattern());
435 } catch (TemplateException e) {
436 return false;
437 }
438 return true;
439 }
440
441 return false;
442 }
443
444 /**
445 * Returns <code>true</code> if a context type id specifies a valid context type
446 * or if no context type registry is present.
447 *
448 * @param contextTypeId the context type id to look for
449 * @return <code>true</code> if the context type specified by the id
450 * is present in the context type registry, or if no registry is
451 * specified
452 */
453 private bool contextExists(String contextTypeId) {
454 return contextTypeId !is null && (fRegistry is null || fRegistry.getContextType(contextTypeId) !is null);
455 }
456
457 /**
458 * Returns the registry.
459 *
460 * @return Returns the registry
461 */
462 protected final ContextTypeRegistry getRegistry() {
463 return fRegistry;
464 }
465 }
466