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