package org.simantics.workbench.search; import java.io.CharArrayWriter; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; import org.simantics.NameLabelMode; import org.simantics.NameLabelUtil; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.exception.DatabaseException; import org.simantics.scl.runtime.function.Function; import org.simantics.scl.runtime.function.Function4; import org.simantics.utils.datastructures.MapList; import org.simantics.utils.datastructures.Pair; import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; import freemarker.template.Template; import freemarker.template.TemplateException; /** * @author Tuukka Lehtonen * @author Marko Luukkainen */ public final class Searching { static final boolean USE_PAGING = false; // create multipage results (currently paging is handled by DataTables (JavaScript)) static final int MAX_PER_PAGE = 100; /** * @param graph * @param query * @throws DatabaseException */ public static Collection> performSearch(ReadGraph graph, Resource searchFunction, Resource model, String query, int maxResults) throws DatabaseException { @SuppressWarnings("unchecked") Function4>> f = graph.adapt(searchFunction, Function.class); Collection> results = f.apply(graph, model, query, maxResults); return results; } /** * @param graph * @param query * @param maxResultsPerModel * @param results * @return * @throws IOException * @throws TemplateException * @throws DatabaseException */ public static List generatePage(ReadGraph graph, Collection searchEngines, SearchQuery query, int maxResultsPerModel, MapList> results) throws IOException, TemplateException, DatabaseException { URL fileUrl = getSearchFileURL(); File dataDir = getAbsoluteFile(fileUrl); SearchData data = new SearchData(); data.dataUrl = fileUrl.toString(); data.dataDirectory = dataDir; data.query = query; data.maxResults = maxResultsPerModel; data.model = null; data.results = new SearchResult(); data.resultCount = 0; data.searchEngines = searchEngines; int resultCount = 0; int resultNumber = 0; int itemsOnPreviousPage = 0; List result = new ArrayList(); QueryResult header = applyTemplate(graph, "header.ftl", data); QueryResult footer = applyTemplate(graph, "footer.ftl", data); StringBuilder body = new StringBuilder(1024 * 80); if (USE_PAGING) { List> allResults = results.getAllValuesSnapshot(); for (Pair entry : allResults) { List items = entry.second.rows; resultCount += items.size(); } data.resultCount = resultCount; for (Resource model : results.getKeys()) { List> modelResults = results.getValues(model); for (Pair entry : modelResults) { List items = entry.second.rows; int start = 0; int count = items.size(); while (count + itemsOnPreviousPage > MAX_PER_PAGE) { int toPage = MAX_PER_PAGE-itemsOnPreviousPage; QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first,entry.second.subset(start, start+toPage)); body.append( content.getHtml() ); result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount)); start += toPage; count -= toPage; itemsOnPreviousPage = 0; body = new StringBuilder(1024 * 80); } if (count > 0) { QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first,entry.second.subset(start, start+count)); body.append( content.getHtml() ); itemsOnPreviousPage += count; } ++resultNumber; } } if (body.length() > 0) result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount)); } else { List> allResults = results.getAllValuesSnapshot(); for (Pair entry : allResults) { List items = entry.second.rows; resultCount += items.size(); } data.resultCount = resultCount; for (Resource model : results.getKeys()) { List> modelResults = results.getValues(model); for (Pair entry : modelResults) { QueryResult content = generateResultTable(graph, query, maxResultsPerModel, data, resultNumber, resultCount, model, entry.first, entry.second); body.append( content.getHtml() ); ++resultNumber; } } result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount)); } if (result.size() == 0) { result.add( new QueryResult(header.getHtml(),body.toString(),footer.getHtml(), resultCount)); } return result; } /** * @param graph * @param query * @param maxResultsPerModel * @param resultNumber * @param results * @return * @throws IOException * @throws TemplateException * @throws DatabaseException */ public static QueryResult generateResultTable(ReadGraph graph, SearchQuery query, int maxResultsPerModel, SearchData template, int resultNumber, int resultCount, Resource model, SearchEngine searchEngine, SearchResult results) throws IOException, TemplateException, DatabaseException { SearchData data = template.clone(); data.resultNumber = resultNumber; data.query = query; data.maxResults = maxResultsPerModel; data.model = NamedResource.of(graph, model); data.results = results; data.searchEngine = searchEngine; return applyTemplate(graph, "searchtable.ftl", data); } /** * @return * @throws IOException */ private static URL getSearchFileURL() throws IOException { Bundle b = Platform.getBundle(Activator.PLUGIN_ID); URL dataUrl = FileLocator.find(b, new Path("search"), null); if (dataUrl == null) throw new IOException("Could not find search template data"); URL fileUrl = FileLocator.toFileURL(dataUrl); return fileUrl; } /** * @return * @throws IOException */ private static File getAbsoluteFile(URL fileUrl) throws IOException { File dataDir = new File(URLDecoder.decode(fileUrl.getPath(), "UTF-8")).getAbsoluteFile(); return dataDir; } /** * @param graph * @param templateName * @param data * @return * @throws IOException * @throws TemplateException * @throws DatabaseException */ public static QueryResult applyTemplate(ReadGraph graph, String templateName, SearchData data) throws IOException, TemplateException, DatabaseException { Configuration cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(data.getDataDirectory()); cfg.setObjectWrapper(new DefaultObjectWrapper()); Template temp = cfg.getTemplate(templateName); CharArrayWriter writer = new CharArrayWriter(1024 * 128); temp.process(data, writer); return new QueryResult(writer.toString(), data.results.rows.size()); } /** * @param graph * @param results * @return map: model -> SearchResult * @throws DatabaseException */ public static final SearchResult generateDependenciesSearchResult(ReadGraph graph, Collection> results) throws DatabaseException { Set processed = new HashSet(); SearchResult result = new SearchResult(NameAndTypeRow.columns); NameLabelMode mode = NameLabelUtil.getNameLabelMode(graph); for (Map r : results) { Resource resource = (Resource) r.get("Resource"); // Prevent index corruption from producing duplicate results. if (!processed.add(resource)) continue; Resource parent = (Resource) r.get("Parent"); String name = (String) r.get("Name"); NameAndTypeRow rst = new NameAndTypeRow(); rst.resource = NamedResource.of(graph, resource, name); rst.parent = NamedResource.of(graph, parent, NameLabelUtil.modalName(graph, parent, mode)); Collection typeResources = graph.getTypes(resource); Collection principalTypeResources = graph.getPrincipalTypes(resource); if (!typeResources.isEmpty()) { rst.types = new ArrayList(typeResources.size()); rst.principalTypes = new ArrayList(principalTypeResources.size()); for (Resource t : typeResources) { NamedResource nr = NamedResource.of(graph, t); rst.types.add(nr); if (principalTypeResources.contains(t)) rst.principalTypes.add(nr); } } result.addRow(rst); } return result; } }