/******************************************************************************* * Copyright (c) 2006 Business Objects Software Limited and others. * All rights reserved. * This file is 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: * Business Objects Software Limited - initial API and implementation *******************************************************************************/ /* * CALSearchResultPage.java * Creation date: Dec 11, 2006. * By: Greg McClement */ package org.openquark.cal.eclipse.ui.search; import java.text.MessageFormat; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.internal.core.JarEntryFile; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.search.ui.text.AbstractTextSearchResult; import org.eclipse.search.ui.text.AbstractTextSearchViewPage; import org.eclipse.search.ui.text.Match; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.model.WorkbenchLabelProvider; import org.eclipse.ui.texteditor.ITextEditor; import org.openquark.cal.compiler.SourceRange; import org.openquark.cal.eclipse.ui.CALEclipseUIPlugin; import org.openquark.cal.eclipse.ui.search.CALSearchPage.CALMatch; import org.openquark.cal.eclipse.ui.search.CALSearchPage.CALSearchResults; import org.openquark.cal.eclipse.ui.util.CoreUtility; /** * @author GMcClement * * Implements a search result page for the Eclipse search view. * */ public class CALSearchResultPage extends AbstractTextSearchViewPage { /** * The content provider for the tree viewer that appears in the search results pane. */ private CALSearchTreeContentProvider contentProvider; protected void elementsChanged(Object[] objects) { if (contentProvider != null) { contentProvider.elementsChanged(objects); } } /* * Shows the given match in the editor. This could convert a CALMatch object that * has line/column notation into offset/length notation. */ protected void showMatch(Match match, int currentOffset, int currentLength, boolean activate) throws PartInitException { IEditorPart editor = null; editor = CoreUtility.openInEditor((IStorage) match.getElement(), false); if (editor != null && activate) { editor.getEditorSite().getPage().activate(editor); } if (editor instanceof ITextEditor) { ITextEditor textEditor = (ITextEditor) editor; if (match instanceof CALMatch) { CALMatch calMatch = (CALMatch) match; if (calMatch.isCharacterPosition()) { textEditor.selectAndReveal(calMatch.getStart(), calMatch.getLength()); } else { SourceRange range = calMatch.getRange(); IDocument doc = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()); try { int start = CoreUtility.convertToCharacterPosition(range.getStartLine(), range.getStartColumn(), doc); int end = CoreUtility.convertToCharacterPosition(range.getEndLine(), range.getEndColumn(), doc); int length = end - start; calMatch.convertToCharacterPosition(start, length); textEditor.selectAndReveal(start, length); } catch (BadLocationException e) { // will only happen on concurrent modification CALEclipseUIPlugin.log(new Status(IStatus.ERROR, CALEclipseUIPlugin.PLUGIN_ID, IStatus.OK, "", e)); //$NON-NLS-1$ } } } else { textEditor.selectAndReveal(currentOffset, currentLength); } } } protected void clear() { if (contentProvider != null) contentProvider.clear(); } protected void configureTreeViewer(TreeViewer viewer) { viewer.setLabelProvider(new CALSearchLabelProvider(this)); contentProvider = new CALSearchTreeContentProvider(viewer); viewer.setContentProvider(contentProvider); viewer.setSorter(new ViewerSorter()); } /** * @author GMcClement * * Provides labels for the matches that appear in the search results pane. */ public static class CALSearchLabelProvider extends LabelProvider { /** * The page that the tree control is on. This is saved so that the * input can be accessed in order to count the number matches. */ private CALSearchResultPage page; private static Image image_cal_file = CALEclipseUIPlugin.getImageDescriptor("/icons/calfile.png").createImage(); /** * Helper class to get icons for standard resources. */ private WorkbenchLabelProvider imageProvider; CALSearchLabelProvider(CALSearchResultPage page) { if (page == null) { throw new IllegalArgumentException(); } // save page because page.getInput() will return null until the page // is configured. this.page = page; this.imageProvider = new WorkbenchLabelProvider(); } public void removeListener(ILabelProviderListener listener) { super.removeListener(listener); imageProvider.removeListener(listener); } public void addListener(ILabelProviderListener listener) { super.addListener(listener); imageProvider.addListener(listener); } public void dispose() { super.dispose(); imageProvider.dispose(); } public String getText(Object element) { if (element instanceof IResource) { IResource resource = (IResource) element; String text = imageProvider.getText(resource); // return text; AbstractTextSearchResult searchResult = page.getInput(); int matchCount = 0; matchCount = searchResult.getMatchCount(element); if (matchCount <= 1) return text; String format = SearchMessages.CALSearchLabelProvider_count_format; return MessageFormat.format(format, new Object[] { text, Integer.valueOf(matchCount) }); } else if (element instanceof JarEntryFile) { // for jar entries return ((JarEntryFile) element).getName(); } else { return null; } } public Image getImage(Object element) { if (element instanceof IResource) { return imageProvider.getImage(element); } else if (element instanceof JarEntryFile) { return image_cal_file; } else { return null; } } } private final static Object[] EMPTY_ARRAY = new Object[0]; /** * @author GMcClement * * Provides the result hierarchy for the tree viewer. Basically the results are show * using the package/file folder hierarchy that the underlying files are stored in. */ public static class CALSearchTreeContentProvider implements ITreeContentProvider { /** * Contains the matches that resulted from the search. */ private CALSearchResults searchResults; private TreeViewer viewer; /** * Maps each object to a set of children of that object. */ private Map<Object, Set<Object>> objectToChildrenMap; CALSearchTreeContentProvider(TreeViewer viewer) { this.viewer = viewer; } void clear(){ // set up the map again initialize(searchResults); // update the UI viewer.refresh(); } public Object[] getChildren(Object parentElement) { Set<Object> children = objectToChildrenMap.get(parentElement); if (children == null) { return EMPTY_ARRAY; } else { return children.toArray(); } } public Object getParent(Object element) { if (element instanceof IProject) { return null; // root } else if (element instanceof IResource) { return ((IResource) element).getParent(); } else { return null; } } public boolean hasChildren(Object element) { Set<Object> children = objectToChildrenMap.get(element); if (children == null) { return false; } else { return children.size() > 0; } } public Object[] getElements(Object inputElement) { return getChildren(inputElement); } public void dispose() { } private void initialize(CALSearchResults searchResults) { if (searchResults == null){ searchResults = null; } else{ this.searchResults = searchResults; objectToChildrenMap = new HashMap<Object, Set<Object>>(); if (searchResults != null) { Object[] elements = searchResults.getElements(); for (int i = 0; i < elements.length; ++i) { insert(elements[i], false); } } } } public synchronized void elementsChanged(Object[] updatedElements) { for (int i = 0; i < updatedElements.length; i++) { if (searchResults.getMatchCount(updatedElements[i]) > 0) insert(updatedElements[i], true); else remove(updatedElements[i], true); } } protected void remove(Object element, boolean refreshViewer) { Object parent = getParent(element); if (parent == null){ if (refreshViewer){ viewer.remove(element); // at the root } } else{ Set<Object> siblings = objectToChildrenMap.get(parent); if (siblings != null) { siblings.remove(element); if (siblings.size() == 0){ remove(parent, refreshViewer); return; } } if (refreshViewer){ viewer.refresh(parent); } } } protected void insert(Object child, boolean refreshViewer) { Object parent = getParent(child); while (parent != null){ if (insert(parent, child)){ if (refreshViewer){ viewer.add(parent, child); } } else{ if (refreshViewer){ viewer.refresh(parent); } return; } child = parent; parent = getParent(child); } if (insert(searchResults, child)){ if (refreshViewer){ viewer.add(searchResults, child); } } } /** * Add the child to the set of children the given parent. * * @param parent * @param child * @return <tt>true</tt> if the set of children did not already contain the specified element. */ private boolean insert(Object parent, Object child) { Set<Object> children = objectToChildrenMap.get(parent); if (children == null){ children = new HashSet<Object>(); objectToChildrenMap.put(parent, children); } return children.add(child); } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput instanceof CALSearchResults) { initialize((CALSearchResults) newInput); } } } public Object[] getElements(Object inputElement) { if (inputElement instanceof AbstractTextSearchResult) { return ((AbstractTextSearchResult) inputElement).getElements(); } else { return null; } } public void dispose() { super.dispose(); contentProvider.dispose(); } protected void configureTableViewer(TableViewer viewer) { } }