1 package org.simantics.workbench.search;
3 import java.io.CharArrayWriter;
5 import java.io.IOException;
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;
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;
29 import freemarker.template.Configuration;
30 import freemarker.template.DefaultObjectWrapper;
31 import freemarker.template.Template;
32 import freemarker.template.TemplateException;
35 * @author Tuukka Lehtonen
36 * @author Marko Luukkainen
38 public final class Searching {
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;
47 * @throws DatabaseException
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);
60 * @param maxResultsPerModel
64 * @throws TemplateException
65 * @throws DatabaseException
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);
72 SearchData data = new SearchData();
73 data.dataUrl = fileUrl.toString();
74 data.dataDirectory = dataDir;
76 data.maxResults = maxResultsPerModel;
78 data.results = new SearchResult();
80 data.searchEngines = searchEngines;
86 int itemsOnPreviousPage = 0;
89 List<QueryResult> result = new ArrayList<QueryResult>();
91 QueryResult header = applyTemplate(graph, "header.ftl", data);
92 QueryResult footer = applyTemplate(graph, "footer.ftl", data);
93 StringBuilder body = new StringBuilder(1024 * 80);
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();
101 data.resultCount = resultCount;
104 for (Resource model : results.getKeys()) {
105 List<Pair<SearchEngine,SearchResult>> modelResults = results.getValues(model);
106 for (Pair<SearchEngine,SearchResult> entry : modelResults) {
109 List<SearchResultRow> items = entry.second.rows;
111 int count = items.size();
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));
120 itemsOnPreviousPage = 0;
121 body = new StringBuilder(1024 * 80);
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;
134 if (body.length() > 0)
135 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
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();
142 data.resultCount = resultCount;
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() );
152 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
154 if (result.size() == 0) {
155 result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount));
166 * @param maxResultsPerModel
167 * @param resultNumber
170 * @throws IOException
171 * @throws TemplateException
172 * @throws DatabaseException
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;
180 data.maxResults = maxResultsPerModel;
181 data.model = NamedResource.of(graph, model);
182 data.results = results;
183 data.searchEngine = searchEngine;
185 return applyTemplate(graph, "searchtable.ftl", data);
190 * @throws IOException
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);
196 throw new IOException("Could not find search template data");
198 URL fileUrl = FileLocator.toFileURL(dataUrl);
204 * @throws IOException
206 private static File getAbsoluteFile(URL fileUrl) throws IOException {
207 File dataDir = new File(URLDecoder.decode(fileUrl.getPath(), "UTF-8")).getAbsoluteFile();
214 * @param templateName
217 * @throws IOException
218 * @throws TemplateException
219 * @throws DatabaseException
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());
227 Template temp = cfg.getTemplate(templateName);
228 CharArrayWriter writer = new CharArrayWriter(1024 * 128);
229 temp.process(data, writer);
231 return new QueryResult(writer.toString(), data.results.rows.size());
237 * @return map: model -> SearchResult
238 * @throws DatabaseException
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);
245 NameLabelMode mode = NameLabelUtil.getNameLabelMode(graph);
247 for (Map<String, Object> r : results) {
248 Resource resource = (Resource) r.get("Resource");
250 // Prevent index corruption from producing duplicate results.
251 if (!processed.add(resource))
254 Resource parent = (Resource) r.get("Parent");
255 String name = (String) r.get("Name");
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));
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);
269 if (principalTypeResources.contains(t))
270 rst.principalTypes.add(nr);