/******************************************************************************* * 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 * Yaroslav Nikolaiko <nikolaiko.yaroslav@gmail.com> - [webapp][base] Bugs related to Search Scope for filtering content in The Eclipse platform's help infocenter - http://bugs.eclipse.org/441407 *******************************************************************************/ package org.eclipse.help.internal.search; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.help.IToc; import org.eclipse.help.ITopic; import org.eclipse.help.base.AbstractHelpScope; import org.eclipse.help.internal.HelpPlugin; import org.eclipse.help.internal.Topic; import org.eclipse.help.internal.base.scope.CriteriaHelpScope; import org.eclipse.help.internal.criteria.CriterionResource; import org.eclipse.help.internal.util.URLCoder; import org.eclipse.help.internal.workingset.AdaptableHelpResource; import org.eclipse.help.internal.workingset.AdaptableSelectedToc; import org.eclipse.help.internal.workingset.AdaptableSelectedTopic; import org.eclipse.help.internal.workingset.AdaptableToc; import org.eclipse.help.internal.workingset.AdaptableTopic; import org.eclipse.help.internal.workingset.WorkingSet; /** * Search result collector. Performs filtering and collects hits into an array * of SearchHit */ public class SearchResults implements ISearchHitCollector { // Collection of AdaptableHelpResource[] private ArrayList<AdaptableHelpResource> scopes; private int maxHits; private String locale; private AbstractHelpScope filter; private CriteriaHelpScope criteriaScope; protected SearchHit[] searchHits = new SearchHit[0]; private QueryTooComplexException searchException = null; private boolean isQuickSearch; public SearchResults(WorkingSet[] workingSets, int maxHits, String locale) { this(workingSets, maxHits, locale, false); } /** * Constructor * * @param workingSets * working sets or null if no filtering */ public SearchResults(WorkingSet[] workingSets, int maxHits, String locale, boolean isQuickSearch) { this.maxHits = maxHits; this.locale = locale; this.scopes = getScopes(workingSets); this.criteriaScope = new CriteriaHelpScope(getCriteriaScopes(workingSets)); this.isQuickSearch = isQuickSearch; } public void setFilter(AbstractHelpScope filter) { this.filter = filter; } /* (non-Javadoc) * @see org.eclipse.help.internal.search.ISearchHitCollector#addHits(List, String) */ @Override public void addHits(List<SearchHit> hits, String highlightTerms) { String urlEncodedWords = URLCoder.encode(highlightTerms); List<SearchHit> searchHitList = new ArrayList<>(); float scoreScale = 1.0f; boolean scoreScaleSet = false; Iterator<SearchHit> iter = hits.iterator(); for (int filteredHits = 0; filteredHits < maxHits && iter.hasNext(); ) { SearchHit rawHit = iter.next(); String href = rawHit.getHref(); IToc toc = null; // the TOC containing the topic AdaptableHelpResource scope = null; // the scope for the topic, if any if (scopes == null) { toc = getTocForTopic(href, locale); if (toc == null && !rawHit.canOpen()) { continue; } } else { scope = getScopeForTopic(href); if (scope == null) { // topic outside of scope continue; } else if ((scope instanceof AdaptableToc) || (scope instanceof AdaptableSelectedToc)) { toc = scope.getAdapter(IToc.class); } else if((scope instanceof AdaptableTopic) || (scope instanceof AdaptableSelectedTopic)){ // scope is AdaptableTopic or AdaptableSelectedTopic toc = scope.getParent().getAdapter(IToc.class); } } // adjust score float score = rawHit.getScore(); if (!scoreScaleSet) { if (score > 0) { scoreScale = 0.99f / score; score = 1; } scoreScaleSet = true; } else { score = score * scoreScale + 0.01f; } // Set the document label String label = rawHit.getLabel(); if ("".equals(label) && toc != null) { //$NON-NLS-1$ ITopic t; if (scope != null) { t = scope.getTopic(href); } else { t = toc.getTopic(href); } if (t != null) { label = t.getLabel(); } } if (label == null || "".equals(label)) { //$NON-NLS-1$ label = href; } // Set document href if (urlEncodedWords.length() > 0) { href += "?resultof=" + urlEncodedWords; //$NON-NLS-1$ } filteredHits ++; searchHitList.add(new SearchHit(href, label, rawHit.getSummary(), score, toc, rawHit.getRawId(), rawHit.getParticipantId(), rawHit.isPotentialHit())); } searchHits = searchHitList .toArray(new SearchHit[searchHitList.size()]); } public void setHits(SearchHit hits[]) { searchHits = hits; } /** * Finds a topic within a scope */ private AdaptableHelpResource getScopeForTopic(String href) { boolean enabled = HelpPlugin.getCriteriaManager().isCriteriaEnabled(); for (int i = 0; i < scopes.size(); i++) { AdaptableHelpResource scope = scopes.get(i); ITopic inScopeTopic = scope.getTopic(href); if (inScopeTopic != null){ if (filter == null || filter.inScope(inScopeTopic)) { if(!enabled || (enabled && criteriaScope.inScope(inScopeTopic))){ return scope; } } } // add root toc's extradir topics to search scope if (!isQuickSearch) { IToc tocRoot = getTocForScope(scope, locale); if (tocRoot != null) { IToc toc = HelpPlugin.getTocManager().getOwningToc(href); if (toc != null) { String owningTocHref = toc.getHref(); if (owningTocHref == tocRoot.getHref()) { Topic extradirTopic = new Topic(); extradirTopic.setHref(href); if (filter == null || filter.inScope(extradirTopic)) { if(!enabled || (enabled && criteriaScope.inScope(inScopeTopic))){ return scope; } } } } } } } return null; } /** * Finds a scope in a toc */ private IToc getTocForScope(AdaptableHelpResource scope, String locale) { if (scope == null) { return null; } String href = scope.getHref(); IToc toc=scope.getAdapter(IToc.class); if (toc != null){ href=toc.getTopic(null).getHref(); } if (href != null && href.length() > 0) { return getTocForTopic(href, locale); } else { AdaptableHelpResource[] childrenScopes = scope.getChildren(); if (childrenScopes != null) { for (int i = 0; i < childrenScopes.length; i++) { // To find the target toc recursively because scope.getHref // may be null. toc = getTocForScope(childrenScopes[i], locale); if (toc != null) return toc; } } } return null; } /** * Finds a topic in a toc or within a scope if specified */ private IToc getTocForTopic(String href, String locale) { IToc[] tocs = HelpPlugin.getTocManager().getTocs(locale); boolean foundInToc = false; for (int i = 0; i < tocs.length; i++) { IToc nextToc = tocs[i]; ITopic topic = nextToc.getTopic(href); if (topic != null) { foundInToc = true; if (filter == null || filter.inScope(topic)) { return nextToc; } } // Test for href attached to Toc element topic = nextToc.getTopic(null); if (topic != null && href != null && href.equals(topic.getHref())) { if (filter == null || filter.inScope(topic)) { return nextToc; } } } if (!foundInToc) { // test to pick up files in extradirs IToc toc = HelpPlugin.getTocManager().getOwningToc(href); if (toc != null) { foundInToc = true; if (filter == null || filter.inScope(toc)) { return toc; } } } return null; } /** * Gets the searchHits. * * @return Returns a SearchHit[] */ public SearchHit[] getSearchHits() { return searchHits; } public QueryTooComplexException getException() { return searchException; } /** * Returns a collection of adaptable help resources that are roots for * filtering. * * @return Collection */ private ArrayList<AdaptableHelpResource> getScopes(WorkingSet[] wSets) { if (wSets == null) return null; scopes = new ArrayList<>(wSets.length); for (int w = 0; w < wSets.length; w++) { AdaptableHelpResource[] elements = wSets[w].getElements(); for (int i = 0; i < elements.length; i++) scopes.add(elements[i]); } return scopes; } private ArrayList<CriterionResource> getCriteriaScopes(WorkingSet[] wSets){ if (wSets == null) return null; ArrayList<CriterionResource> criteriaScopes = new ArrayList<>(wSets.length); for (int w = 0; w < wSets.length; w++) { CriterionResource[] elements = wSets[w].getCriteria(); for (int i = 0; i < elements.length; i++) criteriaScopes.add(elements[i]); } return criteriaScopes; } @Override public void addQTCException(QueryTooComplexException exception) throws QueryTooComplexException { this.searchException = exception; } }