/******************************************************************************* * 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 * 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.valueOf(fElement.getAttribute(SHOW_SCOPE_SECTION_ATTRIBUTE)).booleanValue(); } /** * 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.valueOf(strVal).booleanValue(); } /** * 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.valueOf(fElement.getAttribute(CAN_SEARCH_ENCLOSING_PROJECTS)).booleanValue(); } /** * @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 (int i= 0; i < enabledDescriptors.length; i++) { if (enabledDescriptors[i] instanceof SearchPageDescriptor) fgEnabledPageIds.add(((SearchPageDescriptor)enabledDescriptors[i]).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(); } }