]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.workbench.search/src/org/simantics/workbench/search/Searching.java
Index tokenized lowercase versions of name and types for UI searches
[simantics/platform.git] / bundles / org.simantics.workbench.search / src / org / simantics / workbench / search / Searching.java
1 package org.simantics.workbench.search;
2
3 import java.io.CharArrayWriter;
4 import java.io.File;
5 import java.io.IOException;
6 import java.net.URL;
7 import java.net.URLDecoder;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.HashSet;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14
15 import org.eclipse.core.runtime.FileLocator;
16 import org.eclipse.core.runtime.Path;
17 import org.eclipse.core.runtime.Platform;
18 import org.osgi.framework.Bundle;
19 import org.simantics.NameLabelMode;
20 import org.simantics.NameLabelUtil;
21 import org.simantics.db.ReadGraph;
22 import org.simantics.db.Resource;
23 import org.simantics.db.exception.DatabaseException;
24 import org.simantics.scl.runtime.function.Function;
25 import org.simantics.scl.runtime.function.Function4;
26 import org.simantics.utils.datastructures.MapList;
27 import org.simantics.utils.datastructures.Pair;
28
29 import freemarker.template.Configuration;
30 import freemarker.template.DefaultObjectWrapper;
31 import freemarker.template.Template;
32 import freemarker.template.TemplateException;
33
34 /**
35  * @author Tuukka Lehtonen
36  * @author Marko Luukkainen
37  */
38 public final class Searching {
39         
40         static final boolean USE_PAGING = false; // create multipage results (currently paging is handled by DataTables (JavaScript)) 
41     static final int MAX_PER_PAGE = 100;
42     
43
44     /**
45      * @param graph
46      * @param query
47      * @throws DatabaseException
48      */
49     public static Collection<Map<String, Object>> performSearch(ReadGraph graph, Resource searchFunction, Resource model, String query, int maxResults)
50             throws DatabaseException {
51         @SuppressWarnings("unchecked")
52         Function4<ReadGraph, Resource, String, Integer, Collection<Map<String, Object>>> f = graph.adapt(searchFunction, Function.class);
53         Collection<Map<String, Object>> results = f.apply(graph, model, query, maxResults);
54         return results;
55     }
56
57     /**
58      * @param graph
59      * @param query
60      * @param maxResultsPerModel
61      * @param results
62      * @return
63      * @throws IOException
64      * @throws TemplateException
65      * @throws DatabaseException
66      */
67     public static List<QueryResult> generatePage(ReadGraph graph, Collection<SearchEngine> searchEngines, SearchQuery query, int maxResultsPerModel, 
68             MapList<Resource, Pair<SearchEngine,SearchResult>> results) throws IOException, TemplateException, DatabaseException {
69         URL fileUrl = getSearchFileURL();
70         File dataDir = getAbsoluteFile(fileUrl);
71
72         SearchData data = new SearchData();
73         data.dataUrl = fileUrl.toString();
74         data.dataDirectory = dataDir;
75         data.query = query;
76         data.maxResults = maxResultsPerModel;
77         data.model = null;
78         data.results = new SearchResult();
79         data.resultCount = 0;
80         data.searchEngines = searchEngines;
81
82       
83         int resultCount = 0;
84         int resultNumber = 0;
85         
86         int itemsOnPreviousPage = 0;
87
88         
89         List<QueryResult> result = new ArrayList<QueryResult>();
90         
91         QueryResult header = applyTemplate(graph, "header.ftl", data);
92         QueryResult footer = applyTemplate(graph, "footer.ftl", data);
93         StringBuilder body = new StringBuilder(1024 * 80);
94         
95         if (USE_PAGING) {
96                 List<Pair<SearchEngine, SearchResult>> allResults = results.getAllValuesSnapshot();
97                 for (Pair<SearchEngine,SearchResult> entry : allResults) {
98                          List<SearchResultRow> items = entry.second.rows;
99                          resultCount += items.size();
100                 }
101                 data.resultCount = resultCount;
102                 
103                 
104                 for (Resource model : results.getKeys()) {
105                         List<Pair<SearchEngine,SearchResult>> modelResults = results.getValues(model);
106                         for (Pair<SearchEngine,SearchResult> entry : modelResults) {
107                         
108                         
109                                 List<SearchResultRow> items = entry.second.rows;
110                                 int start = 0;
111                                 int count = items.size();
112                                 
113                                 while (count + itemsOnPreviousPage > MAX_PER_PAGE) {
114                                         int toPage = MAX_PER_PAGE-itemsOnPreviousPage;
115                                         QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first,entry.second.subset(start, start+toPage));
116                                         body.append( content.getHtml() );
117                                         result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
118                                         start += toPage;
119                                         count -= toPage;
120                                         itemsOnPreviousPage = 0;
121                                         body = new StringBuilder(1024 * 80);
122                                 }
123                                 if (count > 0) {
124                                         QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first,entry.second.subset(start, start+count));       
125                                         body.append( content.getHtml() );
126                                         itemsOnPreviousPage += count;
127                                 }
128
129                             ++resultNumber;
130                   
131                     
132                         }
133                 }
134                 if (body.length() > 0)
135                         result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
136         } else {
137                 List<Pair<SearchEngine, SearchResult>> allResults = results.getAllValuesSnapshot();
138                 for (Pair<SearchEngine,SearchResult> entry : allResults) {
139                          List<SearchResultRow> items = entry.second.rows;
140                          resultCount += items.size();
141                 }
142                 data.resultCount = resultCount;
143                 
144                  for (Resource model : results.getKeys()) {
145                          List<Pair<SearchEngine,SearchResult>> modelResults = results.getValues(model);
146                          for (Pair<SearchEngine,SearchResult> entry : modelResults) {
147                                  QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first, entry.second);        
148                                  body.append( content.getHtml() );
149                                  ++resultNumber;
150                         }
151                  }
152                  result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
153         }
154         if (result.size() == 0) {
155                 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
156         }
157
158         return result;
159
160         
161     }
162
163     /**
164      * @param graph
165      * @param query
166      * @param maxResultsPerModel
167      * @param resultNumber 
168      * @param results
169      * @return
170      * @throws IOException
171      * @throws TemplateException
172      * @throws DatabaseException
173      */
174     public static QueryResult generateResultTable(ReadGraph graph, SearchQuery query, int maxResultsPerModel,
175             SearchData template, int resultNumber, int resultCount, Resource model, SearchEngine searchEngine, SearchResult results)
176                     throws IOException, TemplateException, DatabaseException {
177         SearchData data = template.clone();
178         data.resultNumber = resultNumber;
179         data.query = query;
180         data.maxResults = maxResultsPerModel;
181         data.model = NamedResource.of(graph, model);
182         data.results = results;
183         data.searchEngine = searchEngine;
184
185         return applyTemplate(graph, "searchtable.ftl", data);
186     }
187
188     /**
189      * @return
190      * @throws IOException
191      */
192     private static URL getSearchFileURL() throws IOException {
193         Bundle b = Platform.getBundle(Activator.PLUGIN_ID);
194         URL dataUrl = FileLocator.find(b, new Path("search"), null);
195         if (dataUrl == null)
196             throw new IOException("Could not find search template data");
197
198         URL fileUrl = FileLocator.toFileURL(dataUrl);
199         return fileUrl;
200     }
201
202     /**
203      * @return
204      * @throws IOException
205      */
206     private static File getAbsoluteFile(URL fileUrl) throws IOException {
207         File dataDir = new File(URLDecoder.decode(fileUrl.getPath(), "UTF-8")).getAbsoluteFile();
208         return dataDir;
209     }
210
211     
212     /**
213      * @param graph
214      * @param templateName
215      * @param data
216      * @return
217      * @throws IOException
218      * @throws TemplateException
219      * @throws DatabaseException
220      */
221     public static QueryResult applyTemplate(ReadGraph graph, String templateName, SearchData data)
222             throws IOException, TemplateException, DatabaseException {
223         Configuration cfg = new Configuration();
224         cfg.setDirectoryForTemplateLoading(data.getDataDirectory());
225         cfg.setObjectWrapper(new DefaultObjectWrapper());
226
227         Template temp = cfg.getTemplate(templateName);
228         CharArrayWriter writer = new CharArrayWriter(1024 * 128);
229         temp.process(data, writer);
230
231         return new QueryResult(writer.toString(), data.results.rows.size());
232     }
233
234     /**
235      * @param graph
236      * @param results
237      * @return map: model -> SearchResult
238      * @throws DatabaseException
239      */
240     public static final SearchResult generateDependenciesSearchResult(ReadGraph graph,
241             Collection<Map<String, Object>> results) throws DatabaseException {
242         Set<Resource> processed = new HashSet<Resource>();
243         SearchResult result = new SearchResult(NameAndTypeRow.columns);
244
245         NameLabelMode mode = NameLabelUtil.getNameLabelMode(graph);
246
247         for (Map<String, Object> r : results) {
248             Resource resource = (Resource) r.get("Resource");
249
250             // Prevent index corruption from producing duplicate results.
251             if (!processed.add(resource))
252                 continue;
253
254             Resource parent = (Resource) r.get("Parent");
255             String name = (String) r.get("Name");
256
257             NameAndTypeRow rst = new NameAndTypeRow();
258             rst.resource = NamedResource.of(graph, resource, name);
259             rst.parent = NamedResource.of(graph, parent, NameLabelUtil.modalName(graph, parent, mode));
260
261             Collection<Resource> typeResources = graph.getTypes(resource);
262             Collection<Resource> principalTypeResources = graph.getPrincipalTypes(resource);
263             if (!typeResources.isEmpty()) {
264                 rst.types = new ArrayList<NamedResource>(typeResources.size());
265                 rst.principalTypes = new ArrayList<NamedResource>(principalTypeResources.size());
266                 for (Resource t : typeResources) {
267                     NamedResource nr = NamedResource.of(graph, t);
268                     rst.types.add(nr);
269                     if (principalTypeResources.contains(t))
270                         rst.principalTypes.add(nr);
271                 }
272             }
273
274             result.addRow(rst);
275         }
276
277         return result;
278     }
279
280 }