Mercurial > projects > dwt-addons
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 |