KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > search > ui > text > AbstractTextSearchResult


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  *******************************************************************************/

11 package org.eclipse.search.ui.text;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Collection JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20
21 import org.eclipse.search.ui.ISearchResult;
22 import org.eclipse.search.ui.ISearchResultListener;
23 import org.eclipse.search.ui.SearchResultEvent;
24
25 /**
26  * An abstract base implementation for text-match based search results. This search
27  * result implementation consists of a list of {@link org.eclipse.search.ui.text.Match matches}.
28  * No assumptions are made about the kind of elements these matches are reported against.
29  *
30  * @since 3.0
31  */

32 public abstract class AbstractTextSearchResult implements ISearchResult {
33     
34     private static final Match[] EMPTY_ARRAY= new Match[0];
35     
36     private final Map JavaDoc fElementsToMatches;
37     private final List JavaDoc fListeners;
38     private final MatchEvent fMatchEvent;
39
40     private MatchFilter[] fMatchFilters;
41
42     /**
43      * Constructs a new <code>AbstractTextSearchResult</code>
44      */

45     protected AbstractTextSearchResult() {
46         fElementsToMatches= new HashMap JavaDoc();
47         fListeners= new ArrayList JavaDoc();
48         fMatchEvent= new MatchEvent(this);
49         
50         fMatchFilters= null; // filtering disabled by default
51
}
52     
53     /**
54      * Returns an array with all matches reported against the given element.
55      * Note that all matches of the given element are returned. The filter state of the matches is not relevant.
56      *
57      * @param element the element to report matches for
58      * @return all matches reported for this element
59      * @see Match#getElement()
60      */

61     public Match[] getMatches(Object JavaDoc element) {
62         synchronized (fElementsToMatches) {
63             List JavaDoc matches= (List JavaDoc) fElementsToMatches.get(element);
64             if (matches != null)
65                 return (Match[]) matches.toArray(new Match[matches.size()]);
66             return EMPTY_ARRAY;
67         }
68     }
69     
70     /**
71      * Adds a <code>Match</code> to this search result. This method does nothing if the
72      * match is already present.
73      * <p>
74      * Subclasses may extend this method.
75      * </p>
76      *
77      * @param match the match to add
78      */

79     public void addMatch(Match match) {
80         boolean hasAdded= false;
81         synchronized (fElementsToMatches) {
82             hasAdded= doAddMatch(match);
83         }
84         if (hasAdded)
85             fireChange(getSearchResultEvent(match, MatchEvent.ADDED));
86     }
87     
88     /**
89      * Adds a number of Matches to this search result. This method does nothing for
90      * matches that are already present.
91      * <p>
92      * Subclasses may extend this method.
93      * </p>
94      * @param matches the matches to add
95      */

96     public void addMatches(Match[] matches) {
97         Collection JavaDoc reallyAdded= new ArrayList JavaDoc();
98         synchronized (fElementsToMatches) {
99             for (int i = 0; i < matches.length; i++) {
100                 if (doAddMatch(matches[i]))
101                     reallyAdded.add(matches[i]);
102             }
103         }
104         if (!reallyAdded.isEmpty())
105             fireChange(getSearchResultEvent(reallyAdded, MatchEvent.ADDED));
106     }
107     
108     private MatchEvent getSearchResultEvent(Match match, int eventKind) {
109         fMatchEvent.setKind(eventKind);
110         fMatchEvent.setMatch(match);
111         return fMatchEvent;
112     }
113
114     private MatchEvent getSearchResultEvent(Collection JavaDoc matches, int eventKind) {
115         fMatchEvent.setKind(eventKind);
116         Match[] matchArray= (Match[]) matches.toArray(new Match[matches.size()]);
117         fMatchEvent.setMatches(matchArray);
118         return fMatchEvent;
119     }
120
121     private boolean doAddMatch(Match match) {
122         updateFilterState(match);
123         
124         List JavaDoc matches= (List JavaDoc) fElementsToMatches.get(match.getElement());
125         if (matches == null) {
126             matches= new ArrayList JavaDoc();
127             fElementsToMatches.put(match.getElement(), matches);
128             matches.add(match);
129             return true;
130         }
131         if (!matches.contains(match)) {
132             insertSorted(matches, match);
133             return true;
134         }
135         return false;
136     }
137     
138     private static void insertSorted(List JavaDoc matches, Match match) {
139         int insertIndex= getInsertIndex(matches, match);
140         matches.add(insertIndex, match);
141     }
142         
143     private static int getInsertIndex(List JavaDoc matches, Match match) {
144         int count= matches.size();
145         int min = 0, max = count - 1;
146         while (min <= max) {
147             int mid = (min + max) / 2;
148             Match data = (Match) matches.get(mid);
149             int compare = compare(match, data);
150             if (compare > 0)
151                 max = mid - 1;
152             else
153                 min = mid + 1;
154         }
155         return min;
156     }
157
158
159     private static int compare(Match match1, Match match2) {
160         int diff= match2.getOffset()-match1.getOffset();
161         if (diff != 0)
162             return diff;
163         return match2.getLength()-match1.getLength();
164     }
165
166     /**
167      * Removes all matches from this search result.
168      * <p>
169      * Subclasses may extend this method.
170      * </p>
171      */

172     public void removeAll() {
173         synchronized (fElementsToMatches) {
174             doRemoveAll();
175         }
176         fireChange(new RemoveAllEvent(this));
177     }
178     private void doRemoveAll() {
179         fElementsToMatches.clear();
180     }
181     
182     /**
183      * Removes the given match from this search result. This method has no
184      * effect if the match is not found.
185      * <p>
186      * Subclasses may extend this method.
187      * </p>
188      * @param match the match to remove
189      */

190     public void removeMatch(Match match) {
191         boolean existed= false;
192         synchronized (fElementsToMatches) {
193             existed= doRemoveMatch(match);
194         }
195         if (existed)
196             fireChange(getSearchResultEvent(match, MatchEvent.REMOVED));
197     }
198     
199     /**
200      * Removes the given matches from this search result. This method has no
201      * effect for matches that are not found
202      * <p>
203      * Subclasses may extend this method.
204      * </p>
205      *
206      * @param matches the matches to remove
207      */

208     public void removeMatches(Match[] matches) {
209         Collection JavaDoc existing= new ArrayList JavaDoc();
210         synchronized (fElementsToMatches) {
211             for (int i = 0; i < matches.length; i++) {
212                 if (doRemoveMatch(matches[i]))
213                     existing.add(matches[i]); // no duplicate matches at this point
214
}
215         }
216         if (!existing.isEmpty())
217             fireChange(getSearchResultEvent(existing, MatchEvent.REMOVED));
218     }
219
220     
221     private boolean doRemoveMatch(Match match) {
222         boolean existed= false;
223         List JavaDoc matches= (List JavaDoc) fElementsToMatches.get(match.getElement());
224         if (matches != null) {
225             existed= matches.remove(match);
226             if (matches.isEmpty())
227                 fElementsToMatches.remove(match.getElement());
228         }
229         return existed;
230     }
231     
232     /**
233      * {@inheritDoc}
234      */

235     public void addListener(ISearchResultListener l) {
236         synchronized (fListeners) {
237             fListeners.add(l);
238         }
239     }
240     
241     /**
242      * {@inheritDoc}
243      */

244     public void removeListener(ISearchResultListener l) {
245         synchronized (fListeners) {
246             fListeners.remove(l);
247         }
248     }
249     
250     /**
251      * Send the given <code>SearchResultEvent</code> to all registered search
252      * result listeners.
253      *
254      * @param e the event to be sent
255      *
256      * @see ISearchResultListener
257      */

258     protected void fireChange(SearchResultEvent e) {
259         HashSet JavaDoc copiedListeners= new HashSet JavaDoc();
260         synchronized (fListeners) {
261             copiedListeners.addAll(fListeners);
262         }
263         Iterator JavaDoc listeners= copiedListeners.iterator();
264         while (listeners.hasNext()) {
265             ((ISearchResultListener) listeners.next()).searchResultChanged(e);
266         }
267     }
268         
269     private void updateFilterStateForAllMatches() {
270         boolean disableFiltering= getActiveMatchFilters() == null;
271         ArrayList JavaDoc changed= new ArrayList JavaDoc();
272         Object JavaDoc[] elements= getElements();
273         for (int i= 0; i < elements.length; i++) {
274             Match[] matches= getMatches(elements[i]);
275             for (int k= 0; k < matches.length; k++) {
276                 if (disableFiltering || updateFilterState(matches[k])) {
277                     changed.add(matches[k]);
278                 }
279             }
280         }
281         Match[] allChanges= (Match[]) changed.toArray(new Match[changed.size()]);
282         fireChange(new FilterUpdateEvent(this, allChanges, getActiveMatchFilters()));
283     }
284     
285     /*
286      * Evaluates the filter for the match and updates it. Return true if the filter changed.
287      */

288     private boolean updateFilterState(Match match) {
289         MatchFilter[] matchFilters= getActiveMatchFilters();
290         if (matchFilters == null) {
291             return false; // do nothing, no change
292
}
293         
294         boolean oldState= match.isFiltered();
295         for (int i= 0; i < matchFilters.length; i++) {
296             if (matchFilters[i].filters(match)) {
297                 match.setFiltered(true);
298                 return !oldState;
299             }
300         }
301         match.setFiltered(false);
302         return oldState;
303     }
304     
305     /**
306      * Returns the total number of matches contained in this search result.
307      * The filter state of the matches is not relevant when counting matches. All matches are counted.
308      *
309      * @return total number of matches
310      */

311     public int getMatchCount() {
312         int count= 0;
313         synchronized (fElementsToMatches) {
314             for (Iterator JavaDoc elements= fElementsToMatches.values().iterator(); elements.hasNext();) {
315                 List JavaDoc element= (List JavaDoc) elements.next();
316                 if (element != null)
317                     count+= element.size();
318             }
319         }
320         return count;
321     }
322     
323     /**
324      * Returns the number of matches reported against a given element. This is
325      * equivalent to calling <code>getMatches(element).length</code>
326      * The filter state of the matches is not relevant when counting matches. All matches are counted.
327      *
328      * @param element the element to get the match count for
329      * @return the number of matches reported against the element
330      */

331     public int getMatchCount(Object JavaDoc element) {
332         List JavaDoc matches= (List JavaDoc) fElementsToMatches.get(element);
333         if (matches != null)
334             return matches.size();
335         return 0;
336     }
337         
338     /**
339      * Returns an array containing the set of all elements that matches are
340      * reported against in this search result.
341      * Note that all elements that contain matches are returned. The filter state of the matches is not relevant.
342      *
343      * @return the set of elements in this search result
344      */

345     public Object JavaDoc[] getElements() {
346         synchronized (fElementsToMatches) {
347             return fElementsToMatches.keySet().toArray();
348         }
349     }
350     
351     /**
352      * Sets the active match filters for this result. If set to non-null, the match filters will be used to update the filter
353      * state ({@link Match#isFiltered()} of matches and the {@link AbstractTextSearchViewPage} will only
354      * show non-filtered matches. If <code>null</code> is set
355      * the filter state of the match is ignored by the {@link AbstractTextSearchViewPage} and all matches
356      * are shown.
357      * Note the model contains all matches, regardless if the filter state of a match.
358      *
359      * @param filters the match filters to set or <code>null</code> if the filter state of the match
360      * should be ignored.
361      *
362      * @since 3.3
363      */

364     public void setActiveMatchFilters(MatchFilter[] filters) {
365         fMatchFilters= filters;
366         updateFilterStateForAllMatches();
367     }
368     
369     /**
370      * Returns the active match filters for this result. If not null is returned, the match filters will be used to update the filter
371      * state ({@link Match#isFiltered()} of matches and the {@link AbstractTextSearchViewPage} will only
372      * show non-filtered matches. If <code>null</code> is set
373      * the filter state of the match is ignored by the {@link AbstractTextSearchViewPage} and all matches
374      * are shown.
375      *
376      * @return the match filters to be used or <code>null</code> if the filter state of the match
377      * should be ignored.
378      *
379      * @since 3.3
380      */

381     public MatchFilter[] getActiveMatchFilters() {
382         return fMatchFilters;
383     }
384     
385     /**
386      * Returns all applicable filters for this result or null if match filters are not supported. If match filters are returned,
387      * the {@link AbstractTextSearchViewPage} will contain menu entries in the view menu.
388      *
389      * @return all applicable filters for this result.
390      *
391      * @since 3.3
392      */

393     public MatchFilter[] getAllMatchFilters() {
394         return null;
395     }
396     
397
398     /**
399      * Returns an implementation of <code>IEditorMatchAdapter</code> appropriate
400      * for this search result.
401      *
402      * @return an appropriate adapter or <code>null</code> if none has been implemented
403      *
404      * @see IEditorMatchAdapter
405      */

406     public abstract IEditorMatchAdapter getEditorMatchAdapter();
407     
408
409     /**
410      * Returns an implementation of <code>IFileMatchAdapter</code> appropriate
411      * for this search result.
412      *
413      * @return an appropriate adapter or <code>null</code> if none has been implemented
414      *
415      * @see IFileMatchAdapter
416      */

417     public abstract IFileMatchAdapter getFileMatchAdapter();
418 }
419
Popular Tags