blob: d30ee3670d716cb2a840fcd818c96ccb00cef93f [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2015 itemis 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:
* itemis - Initial API and implementation
*
* </copyright>
*/
package org.eclipse.sphinx.emf.search.ui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.ISearchResultListener;
import org.eclipse.search.ui.SearchResultEvent;
import org.eclipse.search.ui.text.RemoveAllEvent;
public class ModelSearchResult implements ISearchResult {
private static final ModelSearchMatch[] EMPTY_ARRAY = new ModelSearchMatch[0];
private ModelSearchQuery query;
private final ListenerList listeners;
private final Map<Object, List<ModelSearchMatch>> elementsToMatches;
private final MatchEvent matchEvent;
public ModelSearchResult(ModelSearchQuery query) {
this.query = query;
listeners = new ListenerList();
elementsToMatches = new HashMap<Object, List<ModelSearchMatch>>();
matchEvent = new MatchEvent(this);
}
/**
* 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 ModelSearchMatch#getElement()
*/
public ModelSearchMatch[] getMatches(Object element) {
synchronized (elementsToMatches) {
List<ModelSearchMatch> matches = elementsToMatches.get(element);
if (matches != null) {
return matches.toArray(new ModelSearchMatch[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(ModelSearchMatch match) {
boolean hasAdded = false;
synchronized (elementsToMatches) {
hasAdded = doAddMatch(match);
}
if (hasAdded) {
fireChange(getSearchResultEvent(match, MatchEvent.ADDED));
}
}
private boolean doAddMatch(ModelSearchMatch match) {
// updateFilterState(match);
List<ModelSearchMatch> matches = elementsToMatches.get(match.getElement());
if (matches == null) {
matches = new ArrayList<ModelSearchMatch>();
elementsToMatches.put(match.getElement(), matches);
matches.add(match);
return true;
}
if (!matches.contains(match)) {
matches.add(match);
return true;
}
return false;
}
/**
* Adds a number of SearchMatches 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(ModelSearchMatch[] matches) {
Collection<ModelSearchMatch> reallyAdded = new ArrayList<ModelSearchMatch>();
synchronized (elementsToMatches) {
for (ModelSearchMatch matche : matches) {
if (doAddMatch(matche)) {
reallyAdded.add(matche);
}
}
}
if (!reallyAdded.isEmpty()) {
fireChange(getSearchResultEvent(reallyAdded, MatchEvent.ADDED));
}
}
/**
* 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 (elementsToMatches) {
return elementsToMatches.keySet().toArray();
}
}
/**
* Removes all matches from this search result.
* <p>
* Subclasses may extend this method.
* </p>
*/
public void removeAll() {
synchronized (elementsToMatches) {
doRemoveAll();
}
fireChange(new RemoveAllEvent(this));
}
private void doRemoveAll() {
elementsToMatches.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(ModelSearchMatch match) {
boolean existed = false;
synchronized (elementsToMatches) {
existed = doRemoveMatch(match);
}
if (existed) {
fireChange(getSearchResultEvent(match, MatchEvent.REMOVED));
}
}
private boolean doRemoveMatch(ModelSearchMatch match) {
boolean existed = false;
List<ModelSearchMatch> matches = elementsToMatches.get(match.getElement());
if (matches != null) {
existed = matches.remove(match);
if (matches.isEmpty()) {
elementsToMatches.remove(match.getElement());
}
}
return existed;
}
/**
* 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(ModelSearchMatch[] matches) {
List<ModelSearchMatch> existing = new ArrayList<ModelSearchMatch>();
synchronized (elementsToMatches) {
for (ModelSearchMatch matche : matches) {
if (doRemoveMatch(matche)) {
existing.add(matche); // no duplicate matches at this point
}
}
}
if (!existing.isEmpty()) {
fireChange(getSearchResultEvent(existing, MatchEvent.REMOVED));
}
}
/**
* 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) {
Object[] listeners2 = listeners.getListeners();
for (Object l : listeners2) {
((ISearchResultListener) l).searchResultChanged(e);
}
}
private MatchEvent getSearchResultEvent(ModelSearchMatch match, int eventKind) {
matchEvent.setKind(eventKind);
matchEvent.setMatch(match);
return matchEvent;
}
private MatchEvent getSearchResultEvent(Collection<ModelSearchMatch> matches, int eventKind) {
matchEvent.setKind(eventKind);
ModelSearchMatch[] matchArray = matches.toArray(new ModelSearchMatch[matches.size()]);
matchEvent.setMatches(matchArray);
return matchEvent;
}
@Override
public void addListener(ISearchResultListener searchResultListener) {
listeners.add(searchResultListener);
}
@Override
public void removeListener(ISearchResultListener searchResultListener) {
listeners.remove(searchResultListener);
}
@Override
public String getLabel() {
return query.getResultLabel(getMatchCount());
}
@Override
public String getTooltip() {
return getLabel();
}
@Override
public ImageDescriptor getImageDescriptor() {
// TODO Auto-generated method stub
return null;
}
@Override
public ISearchQuery getQuery() {
return query;
}
/**
* 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 (elementsToMatches) {
for (List<ModelSearchMatch> matches : elementsToMatches.values()) {
if (matches != null) {
count += matches.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<ModelSearchMatch> matches = elementsToMatches.get(element);
if (matches != null) {
return matches.size();
}
return 0;
}
}