f4ed17de78ce5af9c11e3d78375efa69daab2620
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpdt / ui / actions / CustomFiltersActionGroup.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.ui.actions;
12
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.SortedSet;
22 import java.util.Stack;
23 import java.util.StringTokenizer;
24 import java.util.TreeSet;
25
26 import net.sourceforge.phpdt.core.IJavaModel;
27 import net.sourceforge.phpdt.internal.ui.PHPUiImages;
28 import net.sourceforge.phpdt.internal.ui.filters.CustomFiltersDialog;
29 import net.sourceforge.phpdt.internal.ui.filters.FilterDescriptor;
30 import net.sourceforge.phpdt.internal.ui.filters.FilterMessages;
31 import net.sourceforge.phpdt.internal.ui.filters.NamePatternFilter;
32 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
33 import net.sourceforge.phpeclipse.ui.WebUI;
34
35 import org.eclipse.jface.action.Action;
36 import org.eclipse.jface.action.ContributionItem;
37 import org.eclipse.jface.action.GroupMarker;
38 import org.eclipse.jface.action.IContributionItem;
39 import org.eclipse.jface.action.IMenuListener;
40 import org.eclipse.jface.action.IMenuManager;
41 import org.eclipse.jface.action.IToolBarManager;
42 import org.eclipse.jface.action.Separator;
43 import org.eclipse.jface.preference.IPreferenceStore;
44 import org.eclipse.jface.util.Assert;
45 import org.eclipse.jface.viewers.IContentProvider;
46 import org.eclipse.jface.viewers.ITreeContentProvider;
47 import org.eclipse.jface.viewers.StructuredViewer;
48 import org.eclipse.jface.viewers.ViewerFilter;
49 import org.eclipse.jface.window.Window;
50 import org.eclipse.swt.SWT;
51 import org.eclipse.swt.events.SelectionAdapter;
52 import org.eclipse.swt.events.SelectionEvent;
53 import org.eclipse.swt.widgets.Menu;
54 import org.eclipse.swt.widgets.MenuItem;
55 import org.eclipse.ui.IActionBars;
56 import org.eclipse.ui.IMemento;
57 import org.eclipse.ui.IViewPart;
58 import org.eclipse.ui.actions.ActionGroup;
59
60 /**
61  * Action group to add the filter action to a view part's tool bar menu.
62  * <p>
63  * This class may be instantiated; it is not intended to be subclassed.
64  * </p>
65  * 
66  * @since 2.0
67  */
68 public class CustomFiltersActionGroup extends ActionGroup {
69
70         class ShowFilterDialogAction extends Action {
71                 ShowFilterDialogAction() {
72                         setText(FilterMessages
73                                         .getString("OpenCustomFiltersDialogAction.text")); //$NON-NLS-1$
74                         setImageDescriptor(PHPUiImages.DESC_ELCL_FILTER);
75                         setDisabledImageDescriptor(PHPUiImages.DESC_DLCL_FILTER);
76                 }
77
78                 public void run() {
79                         openDialog();
80                 }
81         }
82
83         /**
84          * Menu contribution item which shows and lets check and uncheck filters.
85          * 
86          * @since 3.0
87          */
88         class FilterActionMenuContributionItem extends ContributionItem {
89
90                 private int fItemNumber;
91
92                 private boolean fState;
93
94                 private String fFilterId;
95
96                 private String fFilterName;
97
98                 private CustomFiltersActionGroup fActionGroup;
99
100                 /**
101                  * Constructor for FilterActionMenuContributionItem.
102                  * 
103                  * @param actionGroup
104                  *            the action group
105                  * @param filterId
106                  *            the id of the filter
107                  * @param filterName
108                  *            the name of the filter
109                  * @param state
110                  *            the initial state of the filter
111                  * @param itemNumber
112                  *            the menu item index
113                  */
114                 public FilterActionMenuContributionItem(
115                                 CustomFiltersActionGroup actionGroup, String filterId,
116                                 String filterName, boolean state, int itemNumber) {
117                         super(filterId);
118                         Assert.isNotNull(actionGroup);
119                         Assert.isNotNull(filterId);
120                         Assert.isNotNull(filterName);
121                         fActionGroup = actionGroup;
122                         fFilterId = filterId;
123                         fFilterName = filterName;
124                         fState = state;
125                         fItemNumber = itemNumber;
126                 }
127
128                 /*
129                  * Overrides method from ContributionItem.
130                  */
131                 public void fill(Menu menu, int index) {
132                         MenuItem mi = new MenuItem(menu, SWT.CHECK, index);
133                         mi.setText("&" + fItemNumber + " " + fFilterName); //$NON-NLS-1$  //$NON-NLS-2$
134                         /*
135                          * XXX: Don't set the image - would look bad because other menu
136                          * items don't provide image XXX: Get working set specific image
137                          * name from XML - would need to cache icons
138                          */
139                         // mi.setImage(JavaPluginImages.get(JavaPluginImages.IMG_OBJS_JAVA_WORKING_SET));
140                         mi.setSelection(fState);
141                         mi.addSelectionListener(new SelectionAdapter() {
142                                 public void widgetSelected(SelectionEvent e) {
143                                         fState = !fState;
144                                         fActionGroup.setFilter(fFilterId, fState);
145                                 }
146                         });
147                 }
148
149                 /*
150                  * @see org.eclipse.jface.action.IContributionItem#isDynamic()
151                  */
152                 public boolean isDynamic() {
153                         return true;
154                 }
155         }
156
157         private static final String TAG_CUSTOM_FILTERS = "customFilters"; //$NON-NLS-1$
158
159         private static final String TAG_USER_DEFINED_PATTERNS_ENABLED = "userDefinedPatternsEnabled"; //$NON-NLS-1$
160
161         private static final String TAG_USER_DEFINED_PATTERNS = "userDefinedPatterns"; //$NON-NLS-1$
162
163         private static final String TAG_XML_DEFINED_FILTERS = "xmlDefinedFilters"; //$NON-NLS-1$
164
165         private static final String TAG_LRU_FILTERS = "lastRecentlyUsedFilters"; //$NON-NLS-1$
166
167         private static final String TAG_CHILD = "child"; //$NON-NLS-1$
168
169         private static final String TAG_PATTERN = "pattern"; //$NON-NLS-1$
170
171         private static final String TAG_FILTER_ID = "filterId"; //$NON-NLS-1$
172
173         private static final String TAG_IS_ENABLED = "isEnabled"; //$NON-NLS-1$
174
175         private static final String SEPARATOR = ","; //$NON-NLS-1$
176
177         private static final int MAX_FILTER_MENU_ENTRIES = 3;
178
179         private static final String RECENT_FILTERS_GROUP_NAME = "recentFiltersGroup"; //$NON-NLS-1$
180
181         private StructuredViewer fViewer;
182
183         private NamePatternFilter fPatternFilter;
184
185         private Map fInstalledBuiltInFilters;
186
187         private Map fEnabledFilterIds;
188
189         private boolean fUserDefinedPatternsEnabled;
190
191         private String[] fUserDefinedPatterns;
192
193         /**
194          * Recently changed filter Ids stack with oldest on top (i.e. at the end).
195          * 
196          * @since 3.0
197          */
198         private Stack fLRUFilterIdsStack;
199
200         /**
201          * Handle to menu manager to dynamically update the last recently used
202          * filters.
203          * 
204          * @since 3.0
205          */
206         private IMenuManager fMenuManager;
207
208         /**
209          * The menu listener which dynamically updates the last recently used
210          * filters.
211          * 
212          * @since 3.0
213          */
214         private IMenuListener fMenuListener;
215
216         /**
217          * Filter Ids used in the last view menu invocation.
218          * 
219          * @since 3.0
220          */
221         private String[] fFilterIdsUsedInLastViewMenu;
222
223         private HashMap fFilterDescriptorMap;
224
225         private String fTargetId;
226
227         /**
228          * Creates a new <code>CustomFiltersActionGroup</code>.
229          * 
230          * @param part
231          *            the view part that owns this action group
232          * @param viewer
233          *            the viewer to be filtered
234          */
235         public CustomFiltersActionGroup(IViewPart part, StructuredViewer viewer) {
236                 this(part.getViewSite().getId(), viewer);
237         }
238
239         /**
240          * Creates a new <code>CustomFiltersActionGroup</code>.
241          * 
242          * @param ownerId
243          *            the id of this action group's owner
244          * @param viewer
245          *            the viewer to be filtered
246          */
247         public CustomFiltersActionGroup(String ownerId, StructuredViewer viewer) {
248                 Assert.isNotNull(ownerId);
249                 Assert.isNotNull(viewer);
250                 fTargetId = ownerId;
251                 fViewer = viewer;
252
253                 fLRUFilterIdsStack = new Stack();
254
255                 initializeWithPluginContributions();
256                 initializeWithViewDefaults();
257
258                 installFilters();
259         }
260
261         /*
262          * Method declared on ActionGroup.
263          */
264         public void fillActionBars(IActionBars actionBars) {
265                 fillToolBar(actionBars.getToolBarManager());
266                 fillViewMenu(actionBars.getMenuManager());
267         }
268
269         public String[] removeFiltersFor(Object parent, Object element,
270                         IContentProvider contentProvider) {
271                 String[] enabledFilters = getEnabledFilterIds();
272                 Set newFilters = new HashSet();
273                 for (int i = 0; i < enabledFilters.length; i++) {
274                         String filterName = enabledFilters[i];
275                         ViewerFilter filter = (ViewerFilter) fInstalledBuiltInFilters
276                                         .get(filterName);
277                         if (filter == null)
278                                 newFilters.add(filterName);
279                         else if (isSelected(parent, element, contentProvider, filter))
280                                 newFilters.add(filterName);
281                 }
282                 if (newFilters.size() == enabledFilters.length)
283                         return new String[0];
284                 return (String[]) newFilters.toArray(new String[newFilters.size()]);
285         }
286
287         public void setFilters(String[] newFilters) {
288                 setEnabledFilterIds(newFilters);
289                 updateViewerFilters(true);
290         }
291
292         private boolean isSelected(Object parent, Object element,
293                         IContentProvider contentProvider, ViewerFilter filter) {
294                 if (contentProvider instanceof ITreeContentProvider) {
295                         // the element and all its parents have to be selected
296                         ITreeContentProvider provider = (ITreeContentProvider) contentProvider;
297                         while (element != null && !(element instanceof IJavaModel)) {
298                                 if (!filter.select(fViewer, parent, element))
299                                         return false;
300                                 element = provider.getParent(element);
301                         }
302                         return true;
303                 }
304                 return filter.select(fViewer, parent, element);
305         }
306
307         /**
308          * Sets the enable state of the given filter.
309          * 
310          * @param filterId
311          *            the id of the filter
312          * @param state
313          *            the filter state
314          */
315         private void setFilter(String filterId, boolean state) {
316                 // Renew filter id in LRU stack
317                 fLRUFilterIdsStack.remove(filterId);
318                 fLRUFilterIdsStack.add(0, filterId);
319
320                 fEnabledFilterIds.put(filterId, new Boolean(state));
321                 storeViewDefaults();
322
323                 updateViewerFilters(true);
324         }
325
326         private String[] getEnabledFilterIds() {
327                 Set enabledFilterIds = new HashSet(fEnabledFilterIds.size());
328                 Iterator iter = fEnabledFilterIds.entrySet().iterator();
329                 while (iter.hasNext()) {
330                         Map.Entry entry = (Map.Entry) iter.next();
331                         String id = (String) entry.getKey();
332                         boolean isEnabled = ((Boolean) entry.getValue()).booleanValue();
333                         if (isEnabled)
334                                 enabledFilterIds.add(id);
335                 }
336                 return (String[]) enabledFilterIds.toArray(new String[enabledFilterIds
337                                 .size()]);
338         }
339
340         private void setEnabledFilterIds(String[] enabledIds) {
341                 Iterator iter = fEnabledFilterIds.keySet().iterator();
342                 while (iter.hasNext()) {
343                         String id = (String) iter.next();
344                         fEnabledFilterIds.put(id, Boolean.FALSE);
345                 }
346                 for (int i = 0; i < enabledIds.length; i++)
347                         fEnabledFilterIds.put(enabledIds[i], Boolean.TRUE);
348         }
349
350         private void setUserDefinedPatterns(String[] patterns) {
351                 fUserDefinedPatterns = patterns;
352                 cleanUpPatternDuplicates();
353         }
354
355         /**
356          * Sets the recently changed filters.
357          * 
358          * @param changeHistory
359          *            the change history
360          * @since 3.0
361          */
362         private void setRecentlyChangedFilters(Stack changeHistory) {
363                 Stack oldestFirstStack = new Stack();
364
365                 int length = Math.min(changeHistory.size(), MAX_FILTER_MENU_ENTRIES);
366                 for (int i = 0; i < length; i++)
367                         oldestFirstStack.push(((FilterDescriptor) changeHistory.pop())
368                                         .getId());
369
370                 length = Math.min(fLRUFilterIdsStack.size(), MAX_FILTER_MENU_ENTRIES
371                                 - oldestFirstStack.size());
372                 int NEWEST = 0;
373                 for (int i = 0; i < length; i++) {
374                         Object filter = fLRUFilterIdsStack.remove(NEWEST);
375                         if (!oldestFirstStack.contains(filter))
376                                 oldestFirstStack.push(filter);
377                 }
378                 fLRUFilterIdsStack = oldestFirstStack;
379         }
380
381         private boolean areUserDefinedPatternsEnabled() {
382                 return fUserDefinedPatternsEnabled;
383         }
384
385         private void setUserDefinedPatternsEnabled(boolean state) {
386                 fUserDefinedPatternsEnabled = state;
387         }
388
389         private void fillToolBar(IToolBarManager tooBar) {
390         }
391
392         /**
393          * Fills the given view menu with the entries managed by the group.
394          * 
395          * @param viewMenu
396          *            the menu to fill
397          */
398         public void fillViewMenu(IMenuManager viewMenu) {
399                 /*
400                  * Don't change the separator group name. Using this name ensures that
401                  * other filters get contributed to the same group.
402                  */
403                 viewMenu.add(new Separator("filters")); //$NON-NLS-1$
404                 viewMenu.add(new GroupMarker(RECENT_FILTERS_GROUP_NAME));
405                 viewMenu.add(new ShowFilterDialogAction());
406
407                 fMenuManager = viewMenu;
408                 fMenuListener = new IMenuListener() {
409                         public void menuAboutToShow(IMenuManager manager) {
410                                 removePreviousLRUFilterActions(manager);
411                                 addLRUFilterActions(manager);
412                         }
413                 };
414                 fMenuManager.addMenuListener(fMenuListener);
415         }
416
417         private void removePreviousLRUFilterActions(IMenuManager mm) {
418                 if (fFilterIdsUsedInLastViewMenu == null)
419                         return;
420
421                 for (int i = 0; i < fFilterIdsUsedInLastViewMenu.length; i++)
422                         mm.remove(fFilterIdsUsedInLastViewMenu[i]);
423         }
424
425         private void addLRUFilterActions(IMenuManager mm) {
426                 if (fLRUFilterIdsStack.isEmpty()) {
427                         fFilterIdsUsedInLastViewMenu = null;
428                         return;
429                 }
430
431                 SortedSet sortedFilters = new TreeSet(fLRUFilterIdsStack);
432                 String[] recentlyChangedFilterIds = (String[]) sortedFilters
433                                 .toArray(new String[sortedFilters.size()]);
434
435                 fFilterIdsUsedInLastViewMenu = new String[recentlyChangedFilterIds.length];
436                 for (int i = 0; i < recentlyChangedFilterIds.length; i++) {
437                         String id = recentlyChangedFilterIds[i];
438                         fFilterIdsUsedInLastViewMenu[i] = id;
439                         boolean state = fEnabledFilterIds.containsKey(id)
440                                         && ((Boolean) fEnabledFilterIds.get(id)).booleanValue();
441                         FilterDescriptor filterDesc = (FilterDescriptor) fFilterDescriptorMap
442                                         .get(id);
443                         if (filterDesc != null) {
444                                 IContributionItem item = new FilterActionMenuContributionItem(
445                                                 this, id, filterDesc.getName(), state, i + 1);
446                                 mm.insertBefore(RECENT_FILTERS_GROUP_NAME, item);
447                         }
448                 }
449         }
450
451         /*
452          * Method declared on ActionGroup.
453          */
454         public void dispose() {
455                 if (fMenuManager != null)
456                         fMenuManager.removeMenuListener(fMenuListener);
457                 super.dispose();
458         }
459
460         private void initializeWithPluginContributions() {
461                 fUserDefinedPatterns = new String[0];
462                 fUserDefinedPatternsEnabled = false;
463
464                 FilterDescriptor[] filterDescs = FilterDescriptor
465                                 .getFilterDescriptors(fTargetId);
466                 fFilterDescriptorMap = new HashMap(filterDescs.length);
467                 fEnabledFilterIds = new HashMap(filterDescs.length);
468                 for (int i = 0; i < filterDescs.length; i++) {
469                         String id = filterDescs[i].getId();
470                         Boolean isEnabled = new Boolean(filterDescs[i].isEnabled());
471                         if (fEnabledFilterIds.containsKey(id))
472                                 WebUI
473                                                 .logErrorMessage("WARNING: Duplicate id for extension-point \"net.sourceforge.phpdt.ui.javaElementFilters\""); //$NON-NLS-1$
474                         fEnabledFilterIds.put(id, isEnabled);
475                         fFilterDescriptorMap.put(id, filterDescs[i]);
476                 }
477         }
478
479         // ---------- viewer filter handling ----------
480
481         private void installFilters() {
482                 fInstalledBuiltInFilters = new HashMap(fEnabledFilterIds.size());
483                 fPatternFilter = new NamePatternFilter();
484                 fPatternFilter.setPatterns(getUserAndBuiltInPatterns());
485                 fViewer.addFilter(fPatternFilter);
486                 updateBuiltInFilters();
487         }
488
489         private void updateViewerFilters(boolean refresh) {
490                 String[] patterns = getUserAndBuiltInPatterns();
491                 fPatternFilter.setPatterns(patterns);
492                 fViewer.getControl().setRedraw(false);
493                 updateBuiltInFilters();
494                 if (refresh)
495                         fViewer.refresh();
496                 fViewer.getControl().setRedraw(true);
497         }
498
499         private void updateBuiltInFilters() {
500                 Set installedFilters = fInstalledBuiltInFilters.keySet();
501                 Set filtersToAdd = new HashSet(fEnabledFilterIds.size());
502                 Set filtersToRemove = new HashSet(fEnabledFilterIds.size());
503                 Iterator iter = fEnabledFilterIds.entrySet().iterator();
504                 while (iter.hasNext()) {
505                         Map.Entry entry = (Map.Entry) iter.next();
506                         String id = (String) entry.getKey();
507                         boolean isEnabled = ((Boolean) entry.getValue()).booleanValue();
508                         if (isEnabled && !installedFilters.contains(id))
509                                 filtersToAdd.add(id);
510                         else if (!isEnabled && installedFilters.contains(id))
511                                 filtersToRemove.add(id);
512                 }
513
514                 // Install the filters
515                 FilterDescriptor[] filterDescs = FilterDescriptor
516                                 .getFilterDescriptors(fTargetId);
517                 for (int i = 0; i < filterDescs.length; i++) {
518                         String id = filterDescs[i].getId();
519                         // just to double check - id should denote a custom filter anyway
520                         boolean isCustomFilter = filterDescs[i].isCustomFilter();
521                         if (isCustomFilter) {
522                                 if (filtersToAdd.contains(id)) {
523                                         ViewerFilter filter = filterDescs[i].createViewerFilter();
524                                         if (filter != null) {
525                                                 fViewer.addFilter(filter);
526                                                 fInstalledBuiltInFilters.put(id, filter);
527                                         }
528                                 }
529                                 if (filtersToRemove.contains(id)) {
530                                         fViewer
531                                                         .removeFilter((ViewerFilter) fInstalledBuiltInFilters
532                                                                         .get(id));
533                                         fInstalledBuiltInFilters.remove(id);
534                                 }
535                         }
536                 }
537         }
538
539         private String[] getUserAndBuiltInPatterns() {
540                 List patterns = new ArrayList(fUserDefinedPatterns.length);
541                 if (areUserDefinedPatternsEnabled())
542                         patterns.addAll(Arrays.asList(fUserDefinedPatterns));
543                 FilterDescriptor[] filterDescs = FilterDescriptor
544                                 .getFilterDescriptors(fTargetId);
545                 for (int i = 0; i < filterDescs.length; i++) {
546                         String id = filterDescs[i].getId();
547                         boolean isPatternFilter = filterDescs[i].isPatternFilter();
548                         Object isEnabled = fEnabledFilterIds.get(id);
549                         if (isEnabled != null && isPatternFilter
550                                         && ((Boolean) isEnabled).booleanValue())
551                                 patterns.add(filterDescs[i].getPattern());
552                 }
553                 return (String[]) patterns.toArray(new String[patterns.size()]);
554         }
555
556         // ---------- view kind/defaults persistency ----------
557
558         private void initializeWithViewDefaults() {
559                 // get default values for view
560                 IPreferenceStore store = WebUI.getDefault()
561                                 .getPreferenceStore();
562
563                 // XXX: can be removed once bug 22533 is fixed.
564                 if (!store.contains(getPreferenceKey("TAG_DUMMY_TO_TEST_EXISTENCE")))//$NON-NLS-1$
565                         return;
566
567                 // XXX: Uncomment once bug 22533 is fixed.
568                 // if
569                 // (!store.contains(getPreferenceKey(TAG_USER_DEFINED_PATTERNS_ENABLED)))
570                 // return;
571
572                 fUserDefinedPatternsEnabled = store
573                                 .getBoolean(getPreferenceKey(TAG_USER_DEFINED_PATTERNS_ENABLED));
574                 setUserDefinedPatterns(CustomFiltersDialog.convertFromString(store
575                                 .getString(getPreferenceKey(TAG_USER_DEFINED_PATTERNS)),
576                                 SEPARATOR));
577
578                 Iterator iter = fEnabledFilterIds.keySet().iterator();
579                 while (iter.hasNext()) {
580                         String id = (String) iter.next();
581                         Boolean isEnabled = new Boolean(store.getBoolean(id));
582                         fEnabledFilterIds.put(id, isEnabled);
583                 }
584
585                 fLRUFilterIdsStack.clear();
586                 String lruFilterIds = store.getString(TAG_LRU_FILTERS);
587                 StringTokenizer tokenizer = new StringTokenizer(lruFilterIds, SEPARATOR);
588                 while (tokenizer.hasMoreTokens()) {
589                         String id = tokenizer.nextToken();
590                         if (fFilterDescriptorMap.containsKey(id)
591                                         && !fLRUFilterIdsStack.contains(id))
592                                 fLRUFilterIdsStack.push(id);
593                 }
594         }
595
596         private void storeViewDefaults() {
597                 // get default values for view
598                 IPreferenceStore store = WebUI.getDefault()
599                                 .getPreferenceStore();
600
601                 // XXX: can be removed once bug 22533 is fixed.
602                 store
603                                 .setValue(
604                                                 getPreferenceKey("TAG_DUMMY_TO_TEST_EXISTENCE"), "storedViewPreferences");//$NON-NLS-1$//$NON-NLS-2$
605
606                 store.setValue(getPreferenceKey(TAG_USER_DEFINED_PATTERNS_ENABLED),
607                                 fUserDefinedPatternsEnabled);
608                 store.setValue(getPreferenceKey(TAG_USER_DEFINED_PATTERNS),
609                                 CustomFiltersDialog.convertToString(fUserDefinedPatterns,
610                                                 SEPARATOR));
611
612                 Iterator iter = fEnabledFilterIds.entrySet().iterator();
613                 while (iter.hasNext()) {
614                         Map.Entry entry = (Map.Entry) iter.next();
615                         String id = (String) entry.getKey();
616                         boolean isEnabled = ((Boolean) entry.getValue()).booleanValue();
617                         store.setValue(id, isEnabled);
618                 }
619
620                 StringBuffer buf = new StringBuffer(fLRUFilterIdsStack.size() * 20);
621                 iter = fLRUFilterIdsStack.iterator();
622                 while (iter.hasNext()) {
623                         buf.append((String) iter.next());
624                         buf.append(SEPARATOR);
625                 }
626                 store.setValue(TAG_LRU_FILTERS, buf.toString());
627         }
628
629         private String getPreferenceKey(String tag) {
630                 return "CustomFiltersActionGroup." + fTargetId + '.' + tag; //$NON-NLS-1$
631         }
632
633         // ---------- view instance persistency ----------
634
635         /**
636          * Saves the state of the custom filters in a memento.
637          * 
638          * @param memento
639          *            the memento into which the state is saved
640          */
641         public void saveState(IMemento memento) {
642                 IMemento customFilters = memento.createChild(TAG_CUSTOM_FILTERS);
643                 customFilters.putString(TAG_USER_DEFINED_PATTERNS_ENABLED, new Boolean(
644                                 fUserDefinedPatternsEnabled).toString());
645                 saveUserDefinedPatterns(customFilters);
646                 saveXmlDefinedFilters(customFilters);
647                 saveLRUFilters(customFilters);
648         }
649
650         private void saveXmlDefinedFilters(IMemento memento) {
651                 if (fEnabledFilterIds != null && !fEnabledFilterIds.isEmpty()) {
652                         IMemento xmlDefinedFilters = memento
653                                         .createChild(TAG_XML_DEFINED_FILTERS);
654                         Iterator iter = fEnabledFilterIds.entrySet().iterator();
655                         while (iter.hasNext()) {
656                                 Map.Entry entry = (Map.Entry) iter.next();
657                                 String id = (String) entry.getKey();
658                                 Boolean isEnabled = (Boolean) entry.getValue();
659                                 IMemento child = xmlDefinedFilters.createChild(TAG_CHILD);
660                                 child.putString(TAG_FILTER_ID, id);
661                                 child.putString(TAG_IS_ENABLED, isEnabled.toString());
662                         }
663                 }
664         }
665
666         /**
667          * Stores the last recently used filter Ids into the given memento
668          * 
669          * @param memento
670          *            the memento into which to store the LRU filter Ids
671          * @since 3.0
672          */
673         private void saveLRUFilters(IMemento memento) {
674                 if (fLRUFilterIdsStack != null && !fLRUFilterIdsStack.isEmpty()) {
675                         IMemento lruFilters = memento.createChild(TAG_LRU_FILTERS);
676                         Iterator iter = fLRUFilterIdsStack.iterator();
677                         while (iter.hasNext()) {
678                                 String id = (String) iter.next();
679                                 IMemento child = lruFilters.createChild(TAG_CHILD);
680                                 child.putString(TAG_FILTER_ID, id);
681                         }
682                 }
683         }
684
685         private void saveUserDefinedPatterns(IMemento memento) {
686                 if (fUserDefinedPatterns != null && fUserDefinedPatterns.length > 0) {
687                         IMemento userDefinedPatterns = memento
688                                         .createChild(TAG_USER_DEFINED_PATTERNS);
689                         for (int i = 0; i < fUserDefinedPatterns.length; i++) {
690                                 IMemento child = userDefinedPatterns.createChild(TAG_CHILD);
691                                 child.putString(TAG_PATTERN, fUserDefinedPatterns[i]);
692                         }
693                 }
694         }
695
696         /**
697          * Restores the state of the filter actions from a memento.
698          * <p>
699          * Note: This method does not refresh the viewer.
700          * </p>
701          * 
702          * @param memento
703          *            the memento from which the state is restored
704          */
705         public void restoreState(IMemento memento) {
706                 if (memento == null)
707                         return;
708                 IMemento customFilters = memento.getChild(TAG_CUSTOM_FILTERS);
709                 if (customFilters == null)
710                         return;
711                 String userDefinedPatternsEnabled = customFilters
712                                 .getString(TAG_USER_DEFINED_PATTERNS_ENABLED);
713                 if (userDefinedPatternsEnabled == null)
714                         return;
715
716                 fUserDefinedPatternsEnabled = Boolean.valueOf(
717                                 userDefinedPatternsEnabled).booleanValue();
718                 restoreUserDefinedPatterns(customFilters);
719                 restoreXmlDefinedFilters(customFilters);
720                 restoreLRUFilters(customFilters);
721
722                 updateViewerFilters(false);
723         }
724
725         private void restoreUserDefinedPatterns(IMemento memento) {
726                 IMemento userDefinedPatterns = memento
727                                 .getChild(TAG_USER_DEFINED_PATTERNS);
728                 if (userDefinedPatterns != null) {
729                         IMemento children[] = userDefinedPatterns.getChildren(TAG_CHILD);
730                         String[] patterns = new String[children.length];
731                         for (int i = 0; i < children.length; i++)
732                                 patterns[i] = children[i].getString(TAG_PATTERN);
733
734                         setUserDefinedPatterns(patterns);
735                 } else
736                         setUserDefinedPatterns(new String[0]);
737         }
738
739         private void restoreXmlDefinedFilters(IMemento memento) {
740                 IMemento xmlDefinedFilters = memento.getChild(TAG_XML_DEFINED_FILTERS);
741                 if (xmlDefinedFilters != null) {
742                         IMemento[] children = xmlDefinedFilters.getChildren(TAG_CHILD);
743                         for (int i = 0; i < children.length; i++) {
744                                 String id = children[i].getString(TAG_FILTER_ID);
745                                 Boolean isEnabled = new Boolean(children[i]
746                                                 .getString(TAG_IS_ENABLED));
747                                 fEnabledFilterIds.put(id, isEnabled);
748                         }
749                 }
750         }
751
752         private void restoreLRUFilters(IMemento memento) {
753                 IMemento lruFilters = memento.getChild(TAG_LRU_FILTERS);
754                 fLRUFilterIdsStack.clear();
755                 if (lruFilters != null) {
756                         IMemento[] children = lruFilters.getChildren(TAG_CHILD);
757                         for (int i = 0; i < children.length; i++) {
758                                 String id = children[i].getString(TAG_FILTER_ID);
759                                 if (fFilterDescriptorMap.containsKey(id)
760                                                 && !fLRUFilterIdsStack.contains(id))
761                                         fLRUFilterIdsStack.push(id);
762                         }
763                 }
764         }
765
766         private void cleanUpPatternDuplicates() {
767                 if (!areUserDefinedPatternsEnabled())
768                         return;
769                 List userDefinedPatterns = new ArrayList(Arrays
770                                 .asList(fUserDefinedPatterns));
771                 FilterDescriptor[] filters = FilterDescriptor
772                                 .getFilterDescriptors(fTargetId);
773
774                 for (int i = 0; i < filters.length; i++) {
775                         if (filters[i].isPatternFilter()) {
776                                 String pattern = filters[i].getPattern();
777                                 if (userDefinedPatterns.contains(pattern)) {
778                                         fEnabledFilterIds.put(filters[i].getId(), Boolean.TRUE);
779                                         boolean hasMore = true;
780                                         while (hasMore)
781                                                 hasMore = userDefinedPatterns.remove(pattern);
782                                 }
783                         }
784                 }
785                 fUserDefinedPatterns = (String[]) userDefinedPatterns
786                                 .toArray(new String[userDefinedPatterns.size()]);
787                 setUserDefinedPatternsEnabled(fUserDefinedPatternsEnabled
788                                 && fUserDefinedPatterns.length > 0);
789         }
790
791         // ---------- dialog related code ----------
792
793         private void openDialog() {
794                 CustomFiltersDialog dialog = new CustomFiltersDialog(fViewer
795                                 .getControl().getShell(), fTargetId,
796                                 areUserDefinedPatternsEnabled(), fUserDefinedPatterns,
797                                 getEnabledFilterIds());
798
799                 if (dialog.open() == Window.OK) {
800                         setEnabledFilterIds(dialog.getEnabledFilterIds());
801                         setUserDefinedPatternsEnabled(dialog
802                                         .areUserDefinedPatternsEnabled());
803                         setUserDefinedPatterns(dialog.getUserDefinedPatterns());
804                         setRecentlyChangedFilters(dialog.getFilterDescriptorChangeHistory());
805
806                         storeViewDefaults();
807
808                         updateViewerFilters(true);
809                 }
810         }
811 }