]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.workbench/src/org/simantics/workbench/internal/contributions/search/SearchServiceImpl.java
Index tokenized lowercase versions of name and types for UI searches
[simantics/platform.git] / bundles / org.simantics.workbench / src / org / simantics / workbench / internal / contributions / search / SearchServiceImpl.java
1 package org.simantics.workbench.internal.contributions.search;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.MalformedURLException;
6 import java.net.URI;
7 import java.net.URISyntaxException;
8 import java.net.URL;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.core.runtime.NullProgressMonitor;
21 import org.eclipse.core.runtime.Status;
22 import org.eclipse.core.runtime.SubMonitor;
23 import org.eclipse.core.runtime.jobs.Job;
24 import org.eclipse.jface.viewers.ISelection;
25 import org.eclipse.jface.viewers.StructuredSelection;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.browser.LocationEvent;
28 import org.eclipse.swt.browser.LocationListener;
29 import org.eclipse.swt.events.DisposeEvent;
30 import org.eclipse.swt.events.DisposeListener;
31 import org.eclipse.swt.widgets.Control;
32 import org.eclipse.swt.widgets.Display;
33 import org.eclipse.swt.widgets.Shell;
34 import org.eclipse.ui.IViewPart;
35 import org.eclipse.ui.IViewReference;
36 import org.eclipse.ui.IWorkbenchPage;
37 import org.eclipse.ui.IWorkbenchWindow;
38 import org.eclipse.ui.PartInitException;
39 import org.eclipse.ui.PlatformUI;
40 import org.simantics.ObjectIdentitySchedulingRule;
41 import org.simantics.Simantics;
42 import org.simantics.databoard.Bindings;
43 import org.simantics.db.ReadGraph;
44 import org.simantics.db.Resource;
45 import org.simantics.db.Session;
46 import org.simantics.db.exception.DatabaseException;
47 import org.simantics.db.layer0.adapter.Instances;
48 import org.simantics.db.layer0.genericrelation.Dependencies;
49 import org.simantics.db.layer0.request.ActiveModels;
50 import org.simantics.db.request.Read;
51 import org.simantics.db.service.SerialisationSupport;
52 import org.simantics.editors.Browser;
53 import org.simantics.editors.BrowserInput;
54 import org.simantics.layer0.Layer0;
55 import org.simantics.scl.runtime.function.Function;
56 import org.simantics.scl.runtime.function.Function5;
57 import org.simantics.ui.workbench.action.ChooseActionRequest;
58 import org.simantics.utils.FileUtils;
59 import org.simantics.utils.datastructures.MapList;
60 import org.simantics.utils.datastructures.Pair;
61 import org.simantics.utils.ui.ErrorLogger;
62 import org.simantics.utils.ui.ExceptionUtils;
63 import org.simantics.utils.ui.workbench.WorkbenchUtils;
64 import org.simantics.workbench.internal.Activator;
65 import org.simantics.workbench.ontology.WorkbenchResource;
66 import org.simantics.workbench.search.ISearchService;
67 import org.simantics.workbench.search.QueryResult;
68 import org.simantics.workbench.search.SearchEngine;
69 import org.simantics.workbench.search.SearchQuery;
70 import org.simantics.workbench.search.SearchResult;
71 import org.simantics.workbench.search.Searching;
72
73 import freemarker.template.TemplateException;
74
75 /**
76  * @author Tuukka Lehtonen
77  * @author Marko Luukkainen
78  */
79 public class SearchServiceImpl implements ISearchService{
80
81         private static final String SEARCH_TEMP_FILE_SUFFIX = "search.html";
82         private static final String SEARCH_TEMP_DIR = "search";
83         private static final String        BROWSER_VIEW = BrowserView.ID;
84         private static final Integer       MAX_RESULTS = 1000;
85
86         @Override
87         public void performQuery(SearchQuery query, ResultBrowser browserType, boolean activateResultBrowser) {
88                 try {
89                         Set<SearchEngine> searchEngines = getSearchEngines();
90                         
91                         // if query does not define used engines, use all available engines.
92                         boolean containsEngines = false;
93                         for (SearchEngine e : searchEngines) {
94                                 if (query.getSearchFlags().containsKey(e.getId())) {
95                                         containsEngines = true;
96                                         break;
97                                 }
98                         }
99                         if (!containsEngines) {
100                                 for (SearchEngine e : searchEngines)
101                                         if (e.isEnabledByDefault())
102                                                 query.setSearchFlag(e.getId(), "on");
103                         }
104                 } catch (DatabaseException e) {
105                         ExceptionUtils.logError(e);
106                 }
107         switch (browserType) {
108             case EDITOR:
109                 performEditorQuery(query);
110                 break;
111             case VIEW:
112                 performViewQuery(query, activateResultBrowser);
113                 break;
114         }
115                 
116         }
117
118          private void performViewQuery(SearchQuery query, boolean activateResultBrowser) {
119                 try {
120                     browserView = (BrowserView) showLocalView(
121                             BROWSER_VIEW,
122                             activateResultBrowser ? IWorkbenchPage.VIEW_ACTIVATE : IWorkbenchPage.VIEW_CREATE);
123                     if (browserView.getBrowser() != browserViewBrowser) {
124                         browserViewBrowser = browserView.getBrowser();
125                         browserViewTemporaryFiles = new ArrayList<File>();
126                         browserViewTemporaryFiles.add(getNewTemporaryFile());
127                         browserView.getBrowser().addLocationListener(browserViewLocationListener);
128                         browserView.getBrowser().addDisposeListener(new DisposeListener() {
129                             @Override
130                             public void widgetDisposed(DisposeEvent e) {
131                                 browserViewBrowser = null;
132                             }
133                         });
134                     }
135                     browserView.setPartProperty(BrowserView.LAST_QUERY_PROPERTY, query.getOriginalQuery());
136                     updateViewQuery(browserView, browserViewTemporaryFiles, query);
137                 } catch (IOException e1) {
138                     ErrorLogger.defaultLogError(e1);
139                 } catch (PartInitException e1) {
140                     ErrorLogger.defaultLogError(e1);
141                 }
142             }
143
144     private IViewPart showLocalView(String id, int mode) throws PartInitException {
145         final IWorkbenchWindow wb = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
146         if (wb == null)
147             return null;
148
149         // Is view already created in the currently active workbench window?
150         IViewPart vp = findViewFromWindow(wb, id, false);
151         if (vp != null) {
152             if (mode == IWorkbenchPage.VIEW_CREATE)
153                 return vp;
154             IWorkbenchPage page = vp.getViewSite().getPage();
155             if (mode == IWorkbenchPage.VIEW_VISIBLE) {
156                 page.bringToTop(vp);
157             } else if (mode == IWorkbenchPage.VIEW_ACTIVATE) {
158                 page.activate(vp);
159             }
160             return vp;
161         }
162
163         // Create the view on the active window's active page
164         IWorkbenchPage page = wb.getActivePage();
165         if (page == null) {
166             IWorkbenchPage pages[] = wb.getPages();
167             if (pages.length == 0) return null;
168             page = pages[0];
169         }
170         return page.showView(id, null, mode);
171     }
172
173     private static IViewPart findViewFromWindow(IWorkbenchWindow wb, String viewId, boolean restore) {
174         for (IWorkbenchPage page : wb.getPages()) {
175             IViewReference vr = page.findViewReference(viewId);
176             if (vr != null) {
177                 IViewPart vp = vr.getView(restore);
178                 if (vp != null)
179                     return vp;
180             }
181         }
182         return null;
183     }
184
185         /**
186              * There's always only one of these around.
187              */
188             BrowserView                     browserView                 = null;
189
190             org.eclipse.swt.browser.Browser browserViewBrowser          = null;
191
192             List<File>                      browserViewTemporaryFiles    = null;
193
194             LocationListener                browserViewLocationListener = new LocationListener() {
195                 @Override
196                 public void changing(LocationEvent event) {
197                     //System.out.println("changing: " + event);
198                     try {
199                         URI newUri = new URI(event.location);
200
201                         // Handle resource links by opening an editor for them
202                         if ("resource".equals(newUri.getScheme())) {
203                             event.doit = false;
204                             openResource(getShell(event), newUri.getSchemeSpecificPart());
205                             return;
206                         }
207                     } catch (URISyntaxException e) {
208                         // This URI was not needed anyway, resource: URIs will always parse properly so let
209                         // it go without logging.
210                     } catch (DatabaseException e) {
211                         ErrorLogger.defaultLogError(e);
212                     }
213                 }
214
215                 @Override
216                 public void changed(LocationEvent event) {
217                     // System.out.println("changed: " + event);
218                     try {
219                         URL newUrl = new URL(event.location);
220                         String query = newUrl.getQuery();
221                         if (query != null) {
222                                 SearchQuery searchQuery = SearchQuery.decode(newUrl);
223                                 if (searchQuery.getOriginalQuery() != null) {
224                                     event.doit = updateViewQuery(
225                                             browserView,
226                                             browserViewTemporaryFiles,
227                                             searchQuery);
228                                 
229                             }
230                         }
231                     } catch (IOException e) {
232                         ErrorLogger.defaultLogError(e);
233                     }
234                 }
235             };
236
237             class ViewQueryJob extends Job {
238
239                 SearchQuery      query;
240
241                 List<File>        temporaryFiles;
242
243                 BrowserView browserView;
244
245                 Display     display;
246
247                 public ViewQueryJob(SearchQuery query, List<File> temporaryFiles, BrowserView browserView) {
248                     super("Search...");
249                     this.query = query;
250                     this.temporaryFiles = temporaryFiles;
251                     this.browserView = browserView;
252                     this.display = browserView.getBrowser().getDisplay();
253                 }
254
255                 @Override
256                 protected IStatus run(IProgressMonitor monitor) {
257                     try {
258                         SubMonitor mon = SubMonitor.convert(monitor);
259                         monitor.beginTask("Performing search '" + query + "'", 10);
260                         monitor.subTask("Querying index");
261                         final List<QueryResult> result = createResultPage(mon.newChild(8), query, MAX_RESULTS);
262                         if (result == null) {
263                             return Status.CANCEL_STATUS;
264                         }
265                         monitor.worked(8);
266                         monitor.subTask("Generating results");
267                         updatePages(result, temporaryFiles);
268                         monitor.worked(2);
269                         updateBrowser(browserView, result.get(0));
270                         return Status.OK_STATUS;
271                     } catch (DatabaseException e1) {
272                         return new Status(IStatus.ERROR, Activator.PLUGIN_ID,
273                                 "Unexpected database problems during search result processing, see exception.", e1);
274                     } catch (IOException e1) {
275                         return new Status(IStatus.ERROR, Activator.PLUGIN_ID,
276                                 "Unexpected I/O problems during search result processing, see exception.", e1);
277                     } finally {
278                         monitor.done();
279                     }
280                 }
281
282                 void updateBrowser(final BrowserView browserView, final QueryResult result) {
283                     if (display.isDisposed())
284                         return;
285                     display.asyncExec(new Runnable() {
286                         @Override
287                         public void run() {
288                             if (browserView.isDisposed())
289                                 return;
290
291                             try {
292                                 long t1 = System.currentTimeMillis();
293                                 browserView.setUrl(temporaryFiles.get(0).toURI().toURL());
294                                 //browserView.setContentDescription("'" + query + "' - " + result.getHitCount() + " matches in active model");
295                                 long t2 = System.currentTimeMillis();
296                                 System.out.println("Updating UI " + (t2-t1) + " ms");
297                             } catch (MalformedURLException e) {
298                                 ErrorLogger.defaultLogError(e);
299                             }
300                         }
301                     });
302                 }
303             }
304
305             protected boolean updateViewQuery(BrowserView browserView, List<File> temporaryFiles, SearchQuery query) {
306                 ViewQueryJob job = new ViewQueryJob(query, temporaryFiles, browserView);
307                 job.setRule(new ObjectIdentitySchedulingRule(browserView));
308                 job.schedule();
309                 return true;
310             }
311
312             protected Shell getShell(LocationEvent event) {
313                 if (event.widget instanceof Control) {
314                     Control c = (Control) event.widget;
315                     if (c.isDisposed())
316                         return null;
317                     return c.getShell();
318                 }
319                 return null;
320             }
321
322             /**
323              * @param query
324              */
325             protected void performEditorQuery(final SearchQuery query) {
326                 try {
327                     final List<File> temporaryFiles = new ArrayList<File>();
328                     temporaryFiles.add(getNewTemporaryFile());
329                     final BrowserInput input = createBrowserInput(temporaryFiles.get(0), query);
330                     updateInput(input, temporaryFiles.get(0), query);
331
332                     final Browser browser = (Browser) WorkbenchUtils.openEditor("org.simantics.editors.browser", input);
333
334                     browser.getBrowser().addLocationListener(new LocationListener() {
335                         @Override
336                         public void changing(LocationEvent event) {
337                             // System.out.println("changing: " + event);
338                             try {
339                                 // TODO : is there a better way to escape location?
340                                 URI newUri = new URI(event.location.replaceAll(" ", "%20"));
341                                 // Handle resource links by opening an editor for them
342                                 if ("resource".equals(newUri.getScheme())) {
343                                     event.doit = false;
344                                     openResource(getShell(event), newUri.getSchemeSpecificPart());
345                                     return;
346                                 }
347                             } catch (URISyntaxException e) {
348                                 ErrorLogger.defaultLogError(e);
349                             } catch (DatabaseException e) {
350                                 ErrorLogger.defaultLogError(e);
351                             }
352                         }
353
354                         @Override
355                         public void changed(LocationEvent event) {
356                             if (query != null) {
357                                 // OpenEditor does not seem to pass any parameters (anything after '?' character) 
358                                 SearchQuery searchQuery = query;
359                                 if (searchQuery.getOriginalQuery() != null) {
360                                     event.doit = updateQuery(browser, input, temporaryFiles, searchQuery);
361                                 }
362                             }
363                         }
364                     });
365                 } catch (IOException e1) {
366                     ErrorLogger.defaultLogError(e1);
367                 } catch (PartInitException e1) {
368                     ErrorLogger.defaultLogError(e1);
369                 }
370             }
371
372             protected void openResource(Shell shell, String link) throws DatabaseException {
373                 try {
374                     Session session = Simantics.getSession();
375                     final long id = Long.parseLong(link);
376
377                     Resource resource = session.syncRequest(new Read<Resource>() {
378                         @Override
379                         public Resource perform(ReadGraph graph) throws DatabaseException {
380                             SerialisationSupport ss = graph.getService(SerialisationSupport.class);
381                             return ss.getResource(id);
382                         }
383                     });
384
385                     ISelection input = new StructuredSelection(resource);
386                     String perspectiveId = WorkbenchUtils.getCurrentPerspectiveId();
387
388                     // Try the doubleClick-extensions
389                     session.asyncRequest(new ChooseActionRequest(shell, input, perspectiveId, false, true));
390                 } catch (NumberFormatException e) {
391                     return;
392                 }
393             }
394
395             /**
396              * @param query
397              */
398             protected static boolean updateQuery(Browser browser, BrowserInput input, List<File> temporaryFiles, final SearchQuery query) {
399                 try {
400                     String url = browser.getBrowser().getUrl();
401                     String inputUrl = input.getUrl().toString();
402                     if (url.equals(inputUrl))
403                         return false;
404
405                     List<QueryResult> result = createResultPage(new NullProgressMonitor(), query, MAX_RESULTS);
406                     updateInput(input, temporaryFiles.get(0), query);
407                     updatePages(result,temporaryFiles);
408                     // browser.getBrowser().setUrl(inputUrl);
409                     browser.getBrowser().refresh();
410                     // temporaryFile.delete();
411                     for (File temporaryFile : temporaryFiles)
412                         temporaryFile.deleteOnExit();
413                     return true;
414                 } catch (DatabaseException e1) {
415                     ErrorLogger.defaultLogError(e1);
416                 } catch (IOException e1) {
417                     ErrorLogger.defaultLogError(e1);
418                 }
419                 return false;
420             }
421
422             private static File ensureTemporaryDirectoryExists() throws IOException {
423                 return Simantics.getTemporaryDirectory(SEARCH_TEMP_DIR); 
424             }
425
426             /**
427              * @param html
428              * @return
429              * @throws IOException
430              */
431             private static File getNewTemporaryFile() throws IOException {
432                 return Simantics.getTempfile(SEARCH_TEMP_DIR, SEARCH_TEMP_FILE_SUFFIX); 
433             }
434
435             /**
436              * @param query
437              * @param html
438              * @return
439              * @throws IOException
440              */
441             private static BrowserInput createBrowserInput(File file, SearchQuery query) throws IOException {
442                 // new BrowserInput(file.toURI().toURL()
443                 return new BrowserInput(SearchQuery.encode(file, query), query.getOriginalQuery(), false, false, SWT.NONE);
444             }
445             
446             private static void updatePages(List<QueryResult> result, List<File> temporaryFiles) throws IOException{
447                 ensureTemporaryDirectoryExists();
448                 for (int i = temporaryFiles.size(); i < result.size(); i++) {
449                         temporaryFiles.add(getNewTemporaryFile());
450                 }
451                 if (result.size() > 1) {
452                         createPageControls(result,temporaryFiles);
453                 }
454                 if (result.size() > 0) {
455                         for (int i = 0; i < result.size(); i++) {
456                                 updatePage(temporaryFiles.get(i), result.get(i).getHtml());
457                         }
458                 } else {
459                         updatePage(temporaryFiles.get(0), "");
460                 }
461             }
462
463             /**
464              * @param html
465              * @return
466              * @throws IOException
467              */
468             private static void updatePage(File file, String html) throws IOException {
469                 long t1 = System.currentTimeMillis();
470                 FileUtils.writeFile(file, html.getBytes("UTF-8"));
471                 long t2 = System.currentTimeMillis();
472                 System.out.println("Writing html page took " + (t2-t1) + " ms");
473             }
474             
475             private static void createPageControls(List<QueryResult> result, List<File> temporaryFiles) throws IOException{
476                 if (result.size() == 1)
477                         return;
478                 for (int i = 0; i < result.size(); i++) {
479                         boolean first = i == 0;
480                         boolean last = i == result.size() -1;
481                         QueryResult current = result.get(i);
482                         String pageContrtol = "<div class=\"resultInfo\">";
483                         pageContrtol += "Page " + (i+1) + " of "+ + result.size() + " ";
484                         if (first) {
485                                 pageContrtol += "First";
486                                 pageContrtol += " Previous";
487                                 pageContrtol += " <a href=\"" + temporaryFiles.get(i+1).toURI().toURL().toString() + "\">Next</a>";
488                                 pageContrtol += " <a href=\"" + temporaryFiles.get(result.size()-1).toURI().toURL().toString() + "\">Last</a>";
489                         } else if (last) {
490                                 pageContrtol += "<a href=\"" + temporaryFiles.get(0).toURI().toURL().toString() + "\">First</a>";
491                                 pageContrtol += " <a href=\"" + temporaryFiles.get(i-1).toURI().toURL().toString() + "\">Previous</a>";
492                                 pageContrtol += " Next";
493                                 pageContrtol += " Last";
494                         } else {
495                                 pageContrtol += "<a href=\"" + temporaryFiles.get(0).toURI().toURL().toString() + "\">First</a>";
496                                 pageContrtol += " <a href=\"" + temporaryFiles.get(i-1).toURI().toURL().toString() + "\">Previous</a>";
497                                 pageContrtol += " <a href=\"" + temporaryFiles.get(i+1).toURI().toURL().toString() + "\">Next</a>";
498                                 pageContrtol += " <a href=\"" + temporaryFiles.get(result.size()-1).toURI().toURL().toString() + "\">Last</a>";
499                         }
500                         pageContrtol += "</div><br>";
501                         
502                         result.set(i, new QueryResult(current.getHeader(), pageContrtol+current.getContent()+pageContrtol,current.getFooter(),current.getHitCount()));
503                 }
504                 
505             }
506             
507             
508
509             /**
510              * @param html
511              * @return
512              * @throws IOException
513              */
514             private static void updateInput(BrowserInput input, File file, SearchQuery query) throws IOException {
515                 URL url = SearchQuery.encode(file, query);
516                 input.setUrl(url);
517                 input.setName(query.getOriginalQuery());
518                 // System.out.println("update input: " + url + " - " + query + " - " + input);
519             }
520             
521             private static Set<SearchEngine> getSearchEngines() throws DatabaseException{
522                   return Simantics.getSession().syncRequest(new Read<Set<SearchEngine>>() {
523                           @Override
524                         public Set<SearchEngine> perform(ReadGraph graph)
525                                         throws DatabaseException {
526                                   Resource project = Simantics.peekProjectResource();
527                                   if (project == null)
528                                           return Collections.emptySet();
529                                   return getSearchEngines(graph, project);
530                         }
531                   });
532             }
533             
534             private static Set<SearchEngine> getSearchEngines(ReadGraph graph, Resource project) throws DatabaseException{
535                 Set<SearchEngine> searchEngines = new HashSet<SearchEngine>();
536                 Map<Resource, Set<SearchEngine>> result = getSearchEnginesByModel(graph, project);
537                 for (Set<SearchEngine> set : result.values())
538                         searchEngines.addAll(set);
539                 return searchEngines;
540             }
541             
542            
543                 private static Map<Resource,Set<SearchEngine>> getSearchEnginesByModel(ReadGraph graph, Resource project) throws DatabaseException{
544                 WorkbenchResource WB = WorkbenchResource.getInstance(graph);
545                 Layer0 L0 = Layer0.getInstance(graph);
546                 Map<Resource, Set<SearchEngine>> result = new HashMap<Resource, Set<SearchEngine>>();
547                 Collection<Resource> activeModels = graph.syncRequest(new ActiveModels(project));
548                 for (Resource model : activeModels) {
549                         Set<SearchEngine> searchEngines = new HashSet<SearchEngine>();
550                         
551                         List<Resource> searchFunctions = new ArrayList<Resource>();
552                     searchFunctions.addAll(graph.getObjects(model, WB.HasWorkbenchSearchFunction));
553                     if (searchFunctions.size() == 0) {
554                         searchEngines.addAll(findSearchContributions(graph, model));
555                     }
556                     if (searchFunctions.isEmpty() && searchEngines.isEmpty()) {
557                         // TODO : backwards compatibility, to be removed.
558                         searchFunctions.addAll(graph.getObjects(project, WB.HasWorkbenchSearchFunction));
559                     }
560                     if (searchFunctions.isEmpty() && searchEngines.isEmpty())
561                         searchFunctions.add( WB.DependenciesSearchFunction);
562
563                     for (Resource searchFunction : searchFunctions) {
564                         @SuppressWarnings("rawtypes")
565                         Function f = graph.adapt(searchFunction, Function.class);
566                         String id = graph.getURI(searchFunction);
567                         String name = graph.getPossibleRelatedValue2(searchFunction, L0.HasLabel);
568                         @SuppressWarnings("unchecked")
569                         SearchEngine engine = new SearchEngine(id,name,(Function5<IProgressMonitor, ReadGraph, Resource, SearchQuery, Integer, SearchResult>)f, true);
570                         {
571                                 engine.addSupportedParam(Dependencies.FIELD_NAME_SEARCH, "Name");
572                                 engine.addSupportedParam(Dependencies.FIELD_TYPES_SEARCH, "Types");
573                         }
574                         searchEngines.add(engine);
575                     }
576                     result.put(model, searchEngines);
577                 }
578                 return result;
579             }
580             
581
582             /**
583              * @param subMonitor 
584              * @param query
585              * @param maxResults
586              * @return
587              * @throws DatabaseException
588              */
589             private static List<QueryResult> createResultPage(final IProgressMonitor monitor, final SearchQuery query, final int maxResults) throws DatabaseException {
590                 return Simantics.getSession().syncRequest(new Read<List<QueryResult>>() {
591                                 @Override
592                     public List<QueryResult> perform(ReadGraph graph) throws DatabaseException {
593                         Resource project = Simantics.peekProjectResource();
594                         if (project == null)
595                             return null;
596
597                         Collection<Resource> activeModels = graph.syncRequest(new ActiveModels(project));
598                         //if (activeModels.isEmpty())
599                         //    return null;
600                         long t1 = System.currentTimeMillis();
601                         Set<SearchEngine> searchEngines = new HashSet<SearchEngine>();
602                         MapList<Resource, Pair<SearchEngine,SearchResult>> searchResults = new MapList<Resource, Pair<SearchEngine,SearchResult>>();
603                         Map<Resource,Set<SearchEngine>> searchEngineMap = getSearchEnginesByModel(graph, project);
604                         
605                         for (Resource model : activeModels) {
606                                 for (SearchEngine engine : searchEngineMap.get(model)) {
607                   
608                                     if (query.getBooleanFlag(engine.getId())) {
609                                             SearchResult result = engine.getSearchFunction().apply(monitor, graph, model, query, maxResults);
610                                             if (!result.isEmpty()) {
611                                                 searchResults.add(model, new Pair<SearchEngine,SearchResult>(engine, result));
612                                             }
613                                     }
614                                     searchEngines.add(engine);
615                             }
616 //                          searchResults.put(model, result);
617                         }
618                         long t2 = System.currentTimeMillis();
619                         System.out.println("Running search queries took " + (t2-t1) + " ms for query '" + query + "'");
620
621                         try {
622                             return Searching.generatePage(graph, searchEngines,query, maxResults, searchResults);
623                         } catch (IOException e) {
624                             Activator.logError("I/O problem while generating search result page.", e);
625                         } catch (TemplateException e) {
626                             Activator.logError("Template definition problem in search result page generation. Please inform the developers.", e);
627                         }
628                         return null;
629                     }
630                 });
631             }
632
633         private static Collection<SearchEngine> findSearchContributions(ReadGraph graph, Resource model) throws DatabaseException {
634                 WorkbenchResource WB = WorkbenchResource.getInstance(graph);
635                 Instances contributionFinder = graph.adapt(WB.SearchContribution, Instances.class);
636                 Resource index = model;
637                 List<SearchEngine> result = new ArrayList<SearchEngine>();
638                 for (Resource r : contributionFinder.find(graph, index)) {
639                         SearchEngine engine = contributionToEngine(graph, r);
640                         if (engine != null)
641                                 result.add(engine);
642                 }
643                 return result;
644         }
645
646         private static SearchEngine contributionToEngine(ReadGraph graph, Resource searchContribution) throws DatabaseException {
647                 Layer0 L0 = Layer0.getInstance(graph);
648                 WorkbenchResource WB = WorkbenchResource.getInstance(graph);
649
650                 Resource searchFunction = graph.getPossibleObject(searchContribution, WB.hasSearchFunction);
651                 if (searchFunction == null)
652                         return null;
653
654                 @SuppressWarnings("rawtypes")
655                 Function f = graph.adapt(searchFunction, Function.class);
656                 String id = graph.getURI(searchFunction);
657                 String name = graph.getPossibleRelatedValue2(searchFunction, L0.HasLabel);
658
659                 Boolean enabledByDefault = graph.getPossibleRelatedValue2(searchContribution, WB.SearchContribution_isEnabledByDefault, Bindings.BOOLEAN);
660                 boolean enabled = !Boolean.FALSE.equals(enabledByDefault);
661
662                 @SuppressWarnings("unchecked")
663                 SearchEngine engine = new SearchEngine(id,name,(Function5<IProgressMonitor, ReadGraph, Resource, SearchQuery, Integer, SearchResult>)f, enabled);
664                 {
665                         engine.addSupportedParam(Dependencies.FIELD_NAME_SEARCH, "Name");
666                         engine.addSupportedParam(Dependencies.FIELD_TYPES_SEARCH, "Types");
667                 }
668                 return engine;
669         }
670
671 }