1 package org.simantics.workbench.search;
\r
3 import java.io.CharArrayWriter;
\r
5 import java.io.IOException;
\r
7 import java.net.URLDecoder;
\r
8 import java.util.ArrayList;
\r
9 import java.util.Collection;
\r
10 import java.util.HashSet;
\r
11 import java.util.List;
\r
12 import java.util.Map;
\r
13 import java.util.Set;
\r
15 import org.eclipse.core.runtime.FileLocator;
\r
16 import org.eclipse.core.runtime.Path;
\r
17 import org.eclipse.core.runtime.Platform;
\r
18 import org.osgi.framework.Bundle;
\r
19 import org.simantics.NameLabelMode;
\r
20 import org.simantics.NameLabelUtil;
\r
21 import org.simantics.db.ReadGraph;
\r
22 import org.simantics.db.Resource;
\r
23 import org.simantics.db.exception.DatabaseException;
\r
24 import org.simantics.scl.runtime.function.Function;
\r
25 import org.simantics.scl.runtime.function.Function4;
\r
26 import org.simantics.utils.datastructures.MapList;
\r
27 import org.simantics.utils.datastructures.Pair;
\r
29 import freemarker.template.Configuration;
\r
30 import freemarker.template.DefaultObjectWrapper;
\r
31 import freemarker.template.Template;
\r
32 import freemarker.template.TemplateException;
\r
35 * @author Tuukka Lehtonen
\r
36 * @author Marko Luukkainen
\r
38 public final class Searching {
\r
40 static final boolean USE_PAGING = false; // create multipage results (currently paging is handled by DataTables (JavaScript))
\r
41 static final int MAX_PER_PAGE = 100;
\r
47 * @throws DatabaseException
\r
49 public static Collection<Map<String, Object>> performSearch(ReadGraph graph, Resource searchFunction, Resource model, String query, int maxResults)
\r
50 throws DatabaseException {
\r
51 @SuppressWarnings("unchecked")
\r
52 Function4<ReadGraph, Resource, String, Integer, Collection<Map<String, Object>>> f = graph.adapt(searchFunction, Function.class);
\r
53 Collection<Map<String, Object>> results = f.apply(graph, model, query, maxResults);
\r
60 * @param maxResultsPerModel
\r
63 * @throws IOException
\r
64 * @throws TemplateException
\r
65 * @throws DatabaseException
\r
67 public static List<QueryResult> generatePage(ReadGraph graph, Collection<SearchEngine> searchEngines, SearchQuery query, int maxResultsPerModel,
\r
68 MapList<Resource, Pair<SearchEngine,SearchResult>> results) throws IOException, TemplateException, DatabaseException {
\r
69 URL fileUrl = getSearchFileURL();
\r
70 File dataDir = getAbsoluteFile(fileUrl);
\r
72 SearchData data = new SearchData();
\r
73 data.dataUrl = fileUrl.toString();
\r
74 data.dataDirectory = dataDir;
\r
76 data.maxResults = maxResultsPerModel;
\r
78 data.results = new SearchResult();
\r
79 data.resultCount = 0;
\r
80 data.searchEngines = searchEngines;
\r
83 int resultCount = 0;
\r
84 int resultNumber = 0;
\r
86 int itemsOnPreviousPage = 0;
\r
89 List<QueryResult> result = new ArrayList<QueryResult>();
\r
91 QueryResult header = applyTemplate(graph, "header.ftl", data);
\r
92 QueryResult footer = applyTemplate(graph, "footer.ftl", data);
\r
93 StringBuilder body = new StringBuilder(1024 * 80);
\r
96 List<Pair<SearchEngine, SearchResult>> allResults = results.getAllValuesSnapshot();
\r
97 for (Pair<SearchEngine,SearchResult> entry : allResults) {
\r
98 List<SearchResultRow> items = entry.second.rows;
\r
99 resultCount += items.size();
\r
101 data.resultCount = resultCount;
\r
104 for (Resource model : results.getKeys()) {
\r
105 List<Pair<SearchEngine,SearchResult>> modelResults = results.getValues(model);
\r
106 for (Pair<SearchEngine,SearchResult> entry : modelResults) {
\r
109 List<SearchResultRow> items = entry.second.rows;
\r
111 int count = items.size();
\r
113 while (count + itemsOnPreviousPage > MAX_PER_PAGE) {
\r
114 int toPage = MAX_PER_PAGE-itemsOnPreviousPage;
\r
115 QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first,entry.second.subset(start, start+toPage));
\r
116 body.append( content.getHtml() );
\r
117 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
\r
120 itemsOnPreviousPage = 0;
\r
121 body = new StringBuilder(1024 * 80);
\r
124 QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first,entry.second.subset(start, start+count));
\r
125 body.append( content.getHtml() );
\r
126 itemsOnPreviousPage += count;
\r
134 if (body.length() > 0)
\r
135 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
\r
137 List<Pair<SearchEngine, SearchResult>> allResults = results.getAllValuesSnapshot();
\r
138 for (Pair<SearchEngine,SearchResult> entry : allResults) {
\r
139 List<SearchResultRow> items = entry.second.rows;
\r
140 resultCount += items.size();
\r
142 data.resultCount = resultCount;
\r
144 for (Resource model : results.getKeys()) {
\r
145 List<Pair<SearchEngine,SearchResult>> modelResults = results.getValues(model);
\r
146 for (Pair<SearchEngine,SearchResult> entry : modelResults) {
\r
147 QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first, entry.second);
\r
148 body.append( content.getHtml() );
\r
152 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
\r
154 if (result.size() == 0) {
\r
155 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
\r
166 * @param maxResultsPerModel
\r
167 * @param resultNumber
\r
170 * @throws IOException
\r
171 * @throws TemplateException
\r
172 * @throws DatabaseException
\r
174 public static QueryResult generateResultTable(ReadGraph graph, SearchQuery query, int maxResultsPerModel,
\r
175 SearchData template, int resultNumber, int resultCount, Resource model, SearchEngine searchEngine, SearchResult results)
\r
176 throws IOException, TemplateException, DatabaseException {
\r
177 SearchData data = template.clone();
\r
178 data.resultNumber = resultNumber;
\r
179 data.query = query;
\r
180 data.maxResults = maxResultsPerModel;
\r
181 data.model = NamedResource.of(graph, model);
\r
182 data.results = results;
\r
183 data.searchEngine = searchEngine;
\r
185 return applyTemplate(graph, "searchtable.ftl", data);
\r
190 * @throws IOException
\r
192 private static URL getSearchFileURL() throws IOException {
\r
193 Bundle b = Platform.getBundle(Activator.PLUGIN_ID);
\r
194 URL dataUrl = FileLocator.find(b, new Path("search"), null);
\r
195 if (dataUrl == null)
\r
196 throw new IOException("Could not find search template data");
\r
198 URL fileUrl = FileLocator.toFileURL(dataUrl);
\r
204 * @throws IOException
\r
206 private static File getAbsoluteFile(URL fileUrl) throws IOException {
\r
207 File dataDir = new File(URLDecoder.decode(fileUrl.getPath(), "UTF-8")).getAbsoluteFile();
\r
214 * @param templateName
\r
217 * @throws IOException
\r
218 * @throws TemplateException
\r
219 * @throws DatabaseException
\r
221 public static QueryResult applyTemplate(ReadGraph graph, String templateName, SearchData data)
\r
222 throws IOException, TemplateException, DatabaseException {
\r
223 Configuration cfg = new Configuration();
\r
224 cfg.setDirectoryForTemplateLoading(data.getDataDirectory());
\r
225 cfg.setObjectWrapper(new DefaultObjectWrapper());
\r
227 Template temp = cfg.getTemplate(templateName);
\r
228 CharArrayWriter writer = new CharArrayWriter(1024 * 128);
\r
229 temp.process(data, writer);
\r
231 return new QueryResult(writer.toString(), data.results.rows.size());
\r
237 * @return map: model -> SearchResult
\r
238 * @throws DatabaseException
\r
240 public static final SearchResult generateDependenciesSearchResult(ReadGraph graph,
\r
241 Collection<Map<String, Object>> results) throws DatabaseException {
\r
242 Set<Resource> processed = new HashSet<Resource>();
\r
243 SearchResult result = new SearchResult(NameAndTypeRow.columns);
\r
245 NameLabelMode mode = NameLabelUtil.getNameLabelMode(graph);
\r
247 for (Map<String, Object> r : results) {
\r
248 Resource resource = (Resource) r.get("Resource");
\r
250 // Prevent index corruption from producing duplicate results.
\r
251 if (!processed.add(resource))
\r
254 Resource parent = (Resource) r.get("Parent");
\r
255 String name = (String) r.get("Name");
\r
257 NameAndTypeRow rst = new NameAndTypeRow();
\r
258 rst.resource = NamedResource.of(graph, resource, name);
\r
259 rst.parent = NamedResource.of(graph, parent, NameLabelUtil.modalName(graph, parent, mode));
\r
261 Collection<Resource> typeResources = graph.getTypes(resource);
\r
262 Collection<Resource> principalTypeResources = graph.getPrincipalTypes(resource);
\r
263 if (!typeResources.isEmpty()) {
\r
264 rst.types = new ArrayList<NamedResource>(typeResources.size());
\r
265 rst.principalTypes = new ArrayList<NamedResource>(principalTypeResources.size());
\r
266 for (Resource t : typeResources) {
\r
267 NamedResource nr = NamedResource.of(graph, t);
\r
269 if (principalTypeResources.contains(t))
\r
270 rst.principalTypes.add(nr);
\r
274 result.addRow(rst);
\r