| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Michael Fraenkel (fraenkel@us.ibm.com) - contributed a fix for: |
| * o Search dialog not respecting activity enablement |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=45729) |
| *******************************************************************************/ |
| package org.eclipse.search.internal.ui; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| |
| import org.osgi.framework.Bundle; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Point; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.StringConverter; |
| |
| import org.eclipse.ui.IPluginContribution; |
| |
| import org.eclipse.search.internal.ui.util.ExceptionHandler; |
| import org.eclipse.search.ui.ISearchPage; |
| import org.eclipse.search.ui.ISearchPageContainer; |
| import org.eclipse.search.ui.ISearchPageScoreComputer; |
| |
| /** |
| * Proxy that represents a search page. |
| */ |
| class SearchPageDescriptor implements IPluginContribution, Comparable<SearchPageDescriptor> { |
| |
| public final static String PAGE_TAG= "page"; //$NON-NLS-1$ |
| private final static String ID_ATTRIBUTE= "id"; //$NON-NLS-1$ |
| private final static String ICON_ATTRIBUTE= "icon"; //$NON-NLS-1$ |
| private final static String CLASS_ATTRIBUTE= "class"; //$NON-NLS-1$ |
| private final static String LABEL_ATTRIBUTE= "label"; //$NON-NLS-1$ |
| private final static String SIZE_ATTRIBUTE= "sizeHint"; //$NON-NLS-1$ |
| private final static String TAB_POSITION_ATTRIBUTE= "tabPosition"; //$NON-NLS-1$ |
| private final static String EXTENSIONS_ATTRIBUTE= "extensions"; //$NON-NLS-1$ |
| private final static String SHOW_SCOPE_SECTION_ATTRIBUTE= "showScopeSection"; //$NON-NLS-1$ |
| private final static String CAN_SEARCH_ENCLOSING_PROJECTS= "canSearchEnclosingProjects"; //$NON-NLS-1$ |
| private final static String ENABLED_ATTRIBUTE= "enabled"; //$NON-NLS-1$ |
| private final static String SEARCH_VIEW_HELP_CONTEXT_ID_ATTRIBUTE= "searchViewHelpContextId"; //$NON-NLS-1$ |
| |
| public final static Point UNKNOWN_SIZE= new Point(SWT.DEFAULT, SWT.DEFAULT); |
| |
| // dialog store id constants |
| private final static String SECTION_ID= "Search"; //$NON-NLS-1$ |
| private final static String STORE_ENABLED_PAGE_IDS= SECTION_ID + ".enabledPageIds"; //$NON-NLS-1$ |
| private final static String STORE_PROCESSED_PAGE_IDS= SECTION_ID + ".processedPageIds"; //$NON-NLS-1$ |
| |
| private static List<String> fgEnabledPageIds; |
| |
| private static class ExtensionScorePair { |
| public String extension; |
| public int score; |
| public ExtensionScorePair(String extension, int score) { |
| this.extension= extension; |
| this.score= score; |
| } |
| } |
| |
| private IConfigurationElement fElement; |
| private List<ExtensionScorePair> fExtensionScorePairs; |
| private int fWildcardScore= ISearchPageScoreComputer.UNKNOWN; |
| private ISearchPage fCreatedPage; |
| |
| /** |
| * Creates a new search page node with the given configuration element. |
| * @param element The configuration element |
| */ |
| public SearchPageDescriptor(IConfigurationElement element) { |
| fElement= element; |
| } |
| |
| /** |
| * Creates a new search page from this node. |
| * @param container The parent container |
| * @return the created page or null if the creation failed |
| * @throws CoreException Page creation failed |
| */ |
| public ISearchPage createObject(ISearchPageContainer container) throws CoreException { |
| if (fCreatedPage == null) { |
| fCreatedPage= (ISearchPage) fElement.createExecutableExtension(CLASS_ATTRIBUTE); |
| fCreatedPage.setTitle(getLabel()); |
| fCreatedPage.setContainer(container); |
| } |
| return fCreatedPage; |
| } |
| |
| public ISearchPage getPage() { |
| return fCreatedPage; |
| } |
| |
| |
| public void dispose() { |
| if (fCreatedPage != null) { |
| fCreatedPage.dispose(); |
| fCreatedPage= null; |
| } |
| } |
| |
| //---- XML Attribute accessors --------------------------------------------- |
| |
| /** |
| * Returns the page's id. |
| * @return The id of the page |
| */ |
| public String getId() { |
| return fElement.getAttribute(ID_ATTRIBUTE); |
| } |
| |
| /** |
| * Returns the page's image |
| * @return ImageDescriptor of the image or null if creating failed |
| */ |
| public ImageDescriptor getImage() { |
| String imageName= fElement.getAttribute(ICON_ATTRIBUTE); |
| if (imageName == null) |
| return null; |
| Bundle bundle = Platform.getBundle(getPluginId()); |
| return SearchPluginImages.createImageDescriptor(bundle, new Path(imageName), true); |
| } |
| |
| /** |
| * @return Returns the page's label. |
| */ |
| public String getLabel() { |
| return fElement.getAttribute(LABEL_ATTRIBUTE); |
| } |
| |
| /** |
| * @return Returns <code>true</code> if the scope section needs |
| * to be shown in the dialog. |
| */ |
| public boolean showScopeSection() { |
| return Boolean.parseBoolean(fElement.getAttribute(SHOW_SCOPE_SECTION_ATTRIBUTE)); |
| } |
| |
| /** |
| * Returns <code>true</code> if the page is initially |
| * shown in the Search dialog. |
| * |
| * This attribute is optional and defaults to <code>true</code>. |
| * @return Returns if the page should be initially shown |
| */ |
| public boolean isInitiallyEnabled() { |
| String strVal= fElement.getAttribute(ENABLED_ATTRIBUTE); |
| return strVal == null || Boolean.parseBoolean(strVal); |
| } |
| |
| /** |
| * Returns <code>true</code> if the page can handle |
| * searches in enclosing projects. The value should be ignored if <code>showScopeSection()</code> |
| * returns <code>false</code>. |
| * |
| * This attribute is optional and defaults to <code>false</code>. |
| * @return Returns if the page can handle searches in enclosing projects |
| */ |
| public boolean canSearchInProjects() { |
| return Boolean.parseBoolean(fElement.getAttribute(CAN_SEARCH_ENCLOSING_PROJECTS)); |
| } |
| |
| /** |
| * @return Returns the page's preferred size |
| */ |
| public Point getPreferredSize() { |
| String sizeHint= fElement.getAttribute(SIZE_ATTRIBUTE); |
| if (sizeHint != null) { |
| int commaSep= sizeHint.indexOf(','); |
| if (commaSep != -1) { |
| try { |
| int xval= Integer.parseInt(sizeHint.substring(0, commaSep).trim()); |
| int yval= Integer.parseInt(sizeHint.substring(commaSep + 1).trim()); |
| return new Point(xval, yval); |
| } catch (NumberFormatException e) { |
| } |
| } |
| } |
| return UNKNOWN_SIZE; |
| } |
| |
| /** |
| * Returns the page's tab position relative to the other tabs. |
| * @return the tab position or <code>Integer.MAX_VALUE</code> if not defined in |
| * the plugins.xml file |
| */ |
| public int getTabPosition() { |
| int position= Integer.MAX_VALUE / 2; |
| String str= fElement.getAttribute(TAB_POSITION_ATTRIBUTE); |
| if (str != null) |
| try { |
| position= Integer.parseInt(str); |
| } catch (NumberFormatException ex) { |
| ExceptionHandler.log(ex, SearchMessages.Search_Error_createSearchPage_message); |
| // position is Integer.MAX_VALUE; |
| } |
| return position; |
| } |
| |
| boolean isEnabled() { |
| return getEnabledPageIds().contains(getId()); |
| } |
| |
| /** |
| * Returns the help context for help shown in search view. |
| * |
| * @return the help context id or <code>null</code> if not defined |
| */ |
| public String getSearchViewHelpContextId() { |
| return fElement.getAttribute(SEARCH_VIEW_HELP_CONTEXT_ID_ATTRIBUTE); |
| } |
| |
| static void setEnabled(Object[] enabledDescriptors) { |
| fgEnabledPageIds= new ArrayList<>(5); |
| for (Object enabledDescriptor : enabledDescriptors) { |
| if (enabledDescriptor instanceof SearchPageDescriptor) |
| fgEnabledPageIds.add(((SearchPageDescriptor)enabledDescriptor).getId()); |
| } |
| storeEnabledPageIds(); |
| } |
| |
| private static List<String> getEnabledPageIds() { |
| if (fgEnabledPageIds == null) { |
| List<SearchPageDescriptor> descriptors= SearchPlugin.getDefault().getSearchPageDescriptors(); |
| |
| String[] enabledPageIds= getDialogSettings().getArray(STORE_ENABLED_PAGE_IDS); |
| if (enabledPageIds == null) |
| fgEnabledPageIds= new ArrayList<>(descriptors.size()); |
| else |
| fgEnabledPageIds= new ArrayList<>(Arrays.asList(enabledPageIds)); |
| |
| |
| List<String> processedPageIds; |
| String[] processedPageIdsArr= getDialogSettings().getArray(STORE_PROCESSED_PAGE_IDS); |
| if (processedPageIdsArr == null) |
| processedPageIds= new ArrayList<>(descriptors.size()); |
| else |
| processedPageIds= new ArrayList<>(Arrays.asList(processedPageIdsArr)); |
| |
| // Enable pages based on contribution |
| Iterator<SearchPageDescriptor> iter= descriptors.iterator(); |
| while (iter.hasNext()) { |
| SearchPageDescriptor desc= iter.next(); |
| if (processedPageIds.contains(desc.getId())) |
| continue; |
| |
| processedPageIds.add(desc.getId()); |
| if (desc.isInitiallyEnabled()) |
| fgEnabledPageIds.add(desc.getId()); |
| } |
| |
| getDialogSettings().put(STORE_PROCESSED_PAGE_IDS, processedPageIds.toArray(new String[processedPageIds.size()])); |
| storeEnabledPageIds(); |
| } |
| return fgEnabledPageIds; |
| } |
| |
| private static void storeEnabledPageIds() { |
| getDialogSettings().put(STORE_ENABLED_PAGE_IDS, fgEnabledPageIds.toArray(new String[fgEnabledPageIds.size()])); |
| } |
| |
| private static IDialogSettings getDialogSettings() { |
| IDialogSettings settings= SearchPlugin.getDefault().getDialogSettings(); |
| IDialogSettings section= settings.getSection(SECTION_ID); |
| if (section == null) |
| // create new section |
| section= settings.addNewSection(SECTION_ID); |
| return section; |
| } |
| |
| @Override |
| public int compareTo(SearchPageDescriptor o) { |
| int myPos= getTabPosition(); |
| int objsPos= o.getTabPosition(); |
| if (myPos == Integer.MAX_VALUE && objsPos == Integer.MAX_VALUE || myPos == objsPos) |
| return getLabel().compareTo(o.getLabel()); |
| |
| return myPos - objsPos; |
| } |
| |
| //---- Suitability tests --------------------------------------------------- |
| |
| /** |
| * Returns the score for this page with the given input element. |
| * @param element The input element |
| * @return The scope for the page |
| */ |
| public int computeScore(Object element) { |
| if (element instanceof IAdaptable) { |
| int score= ISearchPageScoreComputer.UNKNOWN; |
| |
| ISearchPageScoreComputer tester= ((IAdaptable)element).getAdapter(ISearchPageScoreComputer.class); |
| if (tester != null) |
| score= tester.computeScore(getId(), element); |
| |
| IResource resource= ((IAdaptable)element).getAdapter(IResource.class); |
| if (resource != null && resource.getType() == IResource.FILE) { |
| String extension= ((IFile)resource).getFileExtension(); |
| if (extension != null) |
| score= Math.max(score, getScoreForFileExtension(extension)); |
| } |
| if (score != ISearchPageScoreComputer.UNKNOWN) |
| return score; |
| } |
| if (fWildcardScore != ISearchPageScoreComputer.UNKNOWN) |
| return fWildcardScore; |
| |
| return ISearchPageScoreComputer.LOWEST; |
| } |
| |
| private int getScoreForFileExtension(String extension) { |
| if (fExtensionScorePairs == null) |
| readExtensionScorePairs(); |
| |
| int size= fExtensionScorePairs.size(); |
| for (int i= 0; i < size; i++) { |
| ExtensionScorePair p= fExtensionScorePairs.get(i); |
| if (extension.equals(p.extension)) |
| return p.score; |
| } |
| |
| return ISearchPageScoreComputer.UNKNOWN; |
| } |
| |
| private void readExtensionScorePairs() { |
| fExtensionScorePairs= new ArrayList<>(3); |
| String content= fElement.getAttribute(EXTENSIONS_ATTRIBUTE); |
| if (content == null) |
| return; |
| StringTokenizer tokenizer= new StringTokenizer(content, ","); //$NON-NLS-1$ |
| while (tokenizer.hasMoreElements()) { |
| String token= tokenizer.nextToken().trim(); |
| int pos= token.indexOf(':'); |
| if (pos != -1) { |
| String extension= token.substring(0, pos); |
| int score= StringConverter.asInt(token.substring(pos+1).trim(), ISearchPageScoreComputer.UNKNOWN); |
| if (extension.equals("*")) { //$NON-NLS-1$ |
| fWildcardScore= score; |
| } else { |
| fExtensionScorePairs.add(new ExtensionScorePair(extension, score)); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public String getLocalId() { |
| return getId(); |
| } |
| |
| @Override |
| public String getPluginId() { |
| return fElement.getContributor().getName(); |
| } |
| } |