Skip to main content
summaryrefslogblamecommitdiffstats
blob: f4ba3d60c6c18311c80cb356b49348a08c1d0e9c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                                                                
                                                       

                                                                        
                                                           

                                            


                                                                                 
                                   
 
                           
                            




                          
 


                                                   

   
                                                                                   
                                                                                                
                                                                                         

             
   
                                                                         
 
                                                               
 

                                                                  


                                             

           
                                                                 
           
                                              

                                                    
                                                  
 
                                                                     
         
 

                                                                                
                                                                                                                    
          

                                                           
                                  
           
                                                   
                                                   
                                                                             
                                            
                                                                                  
                                           

                 
 
           

                                                                                           
              
                                             
               
          
                                        
           
                                           






                                                                                  
 
           
                                                                                       
                                            
              
                                             
               
                                            
           
                                                 
                                                                 

                                                                  



                                                                    
                                           

                                                                                        
 
                                                                             



                                               
 
                                                                                           
                                               
                                                                               



                                                   
                                                 
                                         
 
                                                                                
                                      
                                                   
                                                                            

                                           

                                               
                                                     



                                    
 
                                                                            
                                                                

                                                
 
                                                                             



                                                  
                                                      






                                                           

         
 






                                                                

                                                       
              
                                             
               
           
                                 







                                                     
 


                                                                              
              
                                             

                                           



                                                   
                                                      



                                                                                    
 


                                                                                
              
                                             
               
          
                                               
           
                                                    
                                                              

                                                                  
                                                              
                                                                                                                     

                         
                                        


                                                                                       
 

                                                    
                                                                                






                                                                              
 
                 




                                                          
 
                 




                                                             
 
           

                                                                                 
          
                                        
          
                                     
           
                                                        
                                                                                


                                                           
                                                                                      
                                             
                                                                

                 
 
                                                       
                                                                          
                                                            








                                                                                        
                                                                               
                                                                                             
         
 



                                                                                                
                                                                    


                                                              
 









                                                              
 

                                                                               
                                                                                                          
          
                                          



                                                   

                                                                                                                           





                                                               
 


                                                                                  
                                                                                                          
          

                                                                     

                                                  
                                                                     



                                              
 


                                                                               
                                                                                                                     
          
                                                            





                                                                     
 
           
                                                                                                                                 




                                                                                                             
          

                                                                                                        
          

                     
                                                                  


                                                 
 
           
                                                                                                                                         



                                                                                                             
          

                                                                                                     
          

                     
                                                      

                                     
 


                                                                                                                                    


                                                          




                                                   
 
 

                                                                                    
                                  
          
                                                                                           
          

                                   
                                                                    
 
 

                                                                                  

                                  
                                                                                           
          

                                 
                                                                
 
/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.search.ui.text;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.ISearchResultListener;
import org.eclipse.search.ui.SearchResultEvent;

/**
 * An abstract base implementation for text-match based search results. This search
 * result implementation consists of a list of {@link org.eclipse.search.ui.text.Match matches}.
 * No assumptions are made about the kind of elements these matches are reported against.
 *
 * @since 3.0
 */
public abstract class AbstractTextSearchResult implements ISearchResult {

	private static final Match[] EMPTY_ARRAY= new Match[0];

	private final Map<Object, List<Match>> fElementsToMatches;
	private final List<ISearchResultListener> fListeners;
	private final MatchEvent fMatchEvent;

	private MatchFilter[] fMatchFilters;

	/**
	 * Constructs a new <code>AbstractTextSearchResult</code>
	 */
	protected AbstractTextSearchResult() {
		fElementsToMatches= new HashMap<>();
		fListeners= new ArrayList<>();
		fMatchEvent= new MatchEvent(this);

		fMatchFilters= null; // filtering disabled by default
	}

	/**
	 * Returns an array with all matches reported against the given element.
	 * Note that all matches of the given element are returned. The filter state of the matches is not relevant.
	 *
	 * @param element the element to report matches for
	 * @return all matches reported for this element
	 * @see Match#getElement()
	 */
	public Match[] getMatches(Object element) {
		synchronized (fElementsToMatches) {
			List<Match> matches= fElementsToMatches.get(element);
			if (matches != null)
				return matches.toArray(new Match[matches.size()]);
			return EMPTY_ARRAY;
		}
	}

	/**
	 * Adds a <code>Match</code> to this search result. This method does nothing if the
	 * match is already present.
	 * <p>
	 * Subclasses may extend this method.
	 * </p>
	 *
	 * @param match the match to add
	 */
	public void addMatch(Match match) {
		boolean hasAdded= false;
		synchronized (fElementsToMatches) {
			hasAdded= doAddMatch(match);
		}
		if (hasAdded)
			fireChange(getSearchResultEvent(match, MatchEvent.ADDED));
	}

	/**
	 * Adds a number of Matches to this search result. This method does nothing for
	 * matches that are already present.
	 * <p>
	 * Subclasses may extend this method.
	 * </p>
	 * @param matches the matches to add
	 */
	public void addMatches(Match[] matches) {
		Collection<Match> reallyAdded= new ArrayList<>();
		synchronized (fElementsToMatches) {
			for (int i = 0; i < matches.length; i++) {
				if (doAddMatch(matches[i]))
					reallyAdded.add(matches[i]);
			}
		}
		if (!reallyAdded.isEmpty())
			fireChange(getSearchResultEvent(reallyAdded, MatchEvent.ADDED));
	}

	private MatchEvent getSearchResultEvent(Match match, int eventKind) {
		fMatchEvent.setKind(eventKind);
		fMatchEvent.setMatch(match);
		return fMatchEvent;
	}

	private MatchEvent getSearchResultEvent(Collection<Match> matches, int eventKind) {
		fMatchEvent.setKind(eventKind);
		Match[] matchArray= matches.toArray(new Match[matches.size()]);
		fMatchEvent.setMatches(matchArray);
		return fMatchEvent;
	}

	private boolean doAddMatch(Match match) {
		updateFilterState(match);

		List<Match> matches= fElementsToMatches.get(match.getElement());
		if (matches == null) {
			matches= new ArrayList<>();
			fElementsToMatches.put(match.getElement(), matches);
			matches.add(match);
			return true;
		}
		if (!matches.contains(match)) {
			insertSorted(matches, match);
			return true;
		}
		return false;
	}

	private static void insertSorted(List<Match> matches, Match match) {
		int insertIndex= getInsertIndex(matches, match);
		matches.add(insertIndex, match);
	}

	private static int getInsertIndex(List<Match> matches, Match match) {
		int count= matches.size();
		int min = 0, max = count - 1;
		while (min <= max) {
			int mid = (min + max) / 2;
			Match data = matches.get(mid);
			int compare = compare(match, data);
			if (compare > 0)
				max = mid - 1;
			else
				min = mid + 1;
		}
		return min;
	}


	private static int compare(Match match1, Match match2) {
		int diff= match2.getOffset()-match1.getOffset();
		if (diff != 0)
			return diff;
		return match2.getLength()-match1.getLength();
	}

	/**
	 * Removes all matches from this search result.
	 * <p>
	 * Subclasses may extend this method.
	 * </p>
	 */
	public void removeAll() {
		synchronized (fElementsToMatches) {
			doRemoveAll();
		}
		fireChange(new RemoveAllEvent(this));
	}
	private void doRemoveAll() {
		fElementsToMatches.clear();
	}

	/**
	 * Removes the given match from this search result. This method has no
	 * effect if the match is not found.
	 * <p>
	 * Subclasses may extend this method.
	 * </p>
	 * @param match the match to remove
	 */
	public void removeMatch(Match match) {
		boolean existed= false;
		synchronized (fElementsToMatches) {
			existed= doRemoveMatch(match);
		}
		if (existed)
			fireChange(getSearchResultEvent(match, MatchEvent.REMOVED));
	}

	/**
	 * Removes the given matches from this search result. This method has no
	 * effect for matches that are not found
	 * <p>
	 * Subclasses may extend this method.
	 * </p>
	 *
	 * @param matches the matches to remove
	 */
	public void removeMatches(Match[] matches) {
		Collection<Match> existing= new ArrayList<>();
		synchronized (fElementsToMatches) {
			for (int i = 0; i < matches.length; i++) {
				if (doRemoveMatch(matches[i]))
					existing.add(matches[i]); 		// no duplicate matches at this point
			}
		}
		if (!existing.isEmpty())
			fireChange(getSearchResultEvent(existing, MatchEvent.REMOVED));
	}


	private boolean doRemoveMatch(Match match) {
		boolean existed= false;
		List<Match> matches= fElementsToMatches.get(match.getElement());
		if (matches != null) {
			existed= matches.remove(match);
			if (matches.isEmpty())
				fElementsToMatches.remove(match.getElement());
		}
		return existed;
	}

	@Override
	public void addListener(ISearchResultListener l) {
		synchronized (fListeners) {
			fListeners.add(l);
		}
	}

	@Override
	public void removeListener(ISearchResultListener l) {
		synchronized (fListeners) {
			fListeners.remove(l);
		}
	}

	/**
	 * Send the given <code>SearchResultEvent</code> to all registered search
	 * result listeners.
	 *
	 * @param e the event to be sent
	 *
	 * @see ISearchResultListener
	 */
	protected void fireChange(SearchResultEvent e) {
		HashSet<ISearchResultListener> copiedListeners= new HashSet<>();
		synchronized (fListeners) {
			copiedListeners.addAll(fListeners);
		}
		Iterator<ISearchResultListener> listeners= copiedListeners.iterator();
		while (listeners.hasNext()) {
			listeners.next().searchResultChanged(e);
		}
	}

	private void updateFilterStateForAllMatches() {
		boolean disableFiltering= getActiveMatchFilters() == null;
		ArrayList<Match> changed= new ArrayList<>();
		Object[] elements= getElements();
		for (int i= 0; i < elements.length; i++) {
			Match[] matches= getMatches(elements[i]);
			for (int k= 0; k < matches.length; k++) {
				if (disableFiltering || updateFilterState(matches[k])) {
					changed.add(matches[k]);
				}
			}
		}
		Match[] allChanges= changed.toArray(new Match[changed.size()]);
		fireChange(new FilterUpdateEvent(this, allChanges, getActiveMatchFilters()));
	}

	/*
	 * Evaluates the filter for the match and updates it. Return true if the filter changed.
	 */
	private boolean updateFilterState(Match match) {
		MatchFilter[] matchFilters= getActiveMatchFilters();
		if (matchFilters == null) {
			return false; // do nothing, no change
		}

		boolean oldState= match.isFiltered();
		for (int i= 0; i < matchFilters.length; i++) {
			if (matchFilters[i].filters(match)) {
				match.setFiltered(true);
				return !oldState;
			}
		}
		match.setFiltered(false);
		return oldState;
	}

	/**
	 * Returns the total number of matches contained in this search result.
	 * The filter state of the matches is not relevant when counting matches. All matches are counted.
	 *
	 * @return total number of matches
	 */
	public int getMatchCount() {
		int count= 0;
		synchronized (fElementsToMatches) {
			for (Iterator<List<Match>> elements= fElementsToMatches.values().iterator(); elements.hasNext();) {
				List<Match> element= elements.next();
				if (element != null)
					count+= element.size();
			}
		}
		return count;
	}

	/**
	 * Returns the number of matches reported against a given element. This is
	 * equivalent to calling <code>getMatches(element).length</code>
	 * The filter state of the matches is not relevant when counting matches. All matches are counted.
	 *
	 * @param element the element to get the match count for
	 * @return the number of matches reported against the element
	 */
	public int getMatchCount(Object element) {
		List<Match> matches= fElementsToMatches.get(element);
		if (matches != null)
			return matches.size();
		return 0;
	}

	/**
	 * Returns an array containing the set of all elements that matches are
	 * reported against in this search result.
	 * Note that all elements that contain matches are returned. The filter state of the matches is not relevant.
	 *
	 * @return the set of elements in this search result
	 */
	public Object[] getElements() {
		synchronized (fElementsToMatches) {
			return fElementsToMatches.keySet().toArray();
		}
	}

	/**
	 * Sets the active match filters for this result. If set to non-null, the match filters will be used to update the filter
	 * state ({@link Match#isFiltered()} of matches and the {@link AbstractTextSearchViewPage} will only
	 * show non-filtered matches. If <code>null</code> is set
	 * the filter state of the match is ignored by the {@link AbstractTextSearchViewPage} and all matches
	 * are shown.
	 * Note the model contains all matches, regardless if the filter state of a match.
	 *
	 * @param filters the match filters to set or <code>null</code> if the filter state of the match
	 * should be ignored.
	 *
	 * @since 3.3
	 */
	public void setActiveMatchFilters(MatchFilter[] filters) {
		fMatchFilters= filters;
		updateFilterStateForAllMatches();
	}

	/**
	 * Returns the active match filters for this result. If not null is returned, the match filters will be used to update the filter
	 * state ({@link Match#isFiltered()} of matches and the {@link AbstractTextSearchViewPage} will only
	 * show non-filtered matches. If <code>null</code> is set
	 * the filter state of the match is ignored by the {@link AbstractTextSearchViewPage} and all matches
	 * are shown.
	 *
	 * @return the match filters to be used or <code>null</code> if the filter state of the match
	 * should be ignored.
	 *
	 * @since 3.3
	 */
	public MatchFilter[] getActiveMatchFilters() {
		return fMatchFilters;
	}

	/**
	 * Returns all applicable filters for this result or null if match filters are not supported. If match filters are returned,
	 * the {@link AbstractTextSearchViewPage} will contain menu entries in the view menu.
	 *
	 * @return all applicable filters for this result.
	 *
	 * @since 3.3
	 */
	public MatchFilter[] getAllMatchFilters() {
		return null;
	}


	/**
	 * Returns an implementation of <code>IEditorMatchAdapter</code> appropriate
	 * for this search result.
	 *
	 * @return an appropriate adapter or <code>null</code> if none has been implemented
	 *
	 * @see IEditorMatchAdapter
	 */
	public abstract IEditorMatchAdapter getEditorMatchAdapter();


	/**
	 * Returns an implementation of <code>IFileMatchAdapter</code> appropriate
	 * for this search result.
	 *
	 * @return an appropriate adapter or <code>null</code> if none has been implemented
	 *
	 * @see IFileMatchAdapter
	 */
	public abstract IFileMatchAdapter getFileMatchAdapter();
}

Back to the top