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