001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package plugins.kernel.searchprovider;
020
021import icy.gui.plugin.PluginDetailPanel;
022import icy.main.Icy;
023import icy.network.NetworkUtil;
024import icy.plugin.PluginDescriptor;
025import icy.plugin.PluginInstaller;
026import icy.plugin.PluginLauncher;
027import icy.plugin.PluginLoader;
028import icy.plugin.PluginRepositoryLoader;
029import icy.search.OnlineSearchResultProducer;
030import icy.search.SearchEngine;
031import icy.search.SearchResult;
032import icy.search.SearchResultConsumer;
033import icy.search.SearchResultProducer;
034import icy.system.thread.ThreadUtil;
035import icy.util.XMLUtil;
036
037import java.util.ArrayList;
038import java.util.List;
039
040import org.w3c.dom.Document;
041import org.w3c.dom.Element;
042
043import plugins.kernel.searchprovider.LocalPluginSearchResultProducer.LocalPluginResult;
044import plugins.kernel.searchprovider.PluginSearchResultProducerHelper.SearchWord;
045
046/**
047 * This class is used to provide online plugin elements to the search engine.
048 * 
049 * @author Stephane
050 */
051public class OnlinePluginSearchResultProducer extends OnlineSearchResultProducer
052{
053    /**
054     * @author Stephane
055     */
056    public static class OnlinePluginResult extends PluginSearchResult
057    {
058        public OnlinePluginResult(SearchResultProducer provider, PluginDescriptor plugin, String text,
059                List<SearchWord> searchWords, int priority)
060        {
061            super(provider, plugin, text, searchWords, priority);
062        }
063
064        @Override
065        public String getTooltip()
066        {
067            return "Left click: Install and Run   -   Right click: Online documentation";
068        }
069
070        @Override
071        public void execute()
072        {
073            // can take sometime, better to execute it in background
074            ThreadUtil.bgRun(new Runnable()
075            {
076                @Override
077                public void run()
078                {
079                    // plugin locally installed ? (result transfer to local plugin provider)
080                    if (PluginLoader.isLoaded(plugin.getClassName()))
081                    {
082                        if (plugin.isActionable())
083                            PluginLauncher.start(plugin);
084                        else
085                        {
086                            ThreadUtil.invokeLater(new Runnable()
087                            {
088                                @Override
089                                public void run()
090                                {
091                                    new PluginDetailPanel(plugin);
092                                }
093                            });
094                        }
095                    }
096                    else
097                    {
098                        // install and run the plugin (if user ok with install)
099                        PluginInstaller.install(plugin, true);
100                        // wait for installation complete
101                        PluginInstaller.waitInstall();
102
103                        // find the new installed plugin
104                        final PluginDescriptor localPlugin = PluginLoader.getPlugin(plugin.getClassName());
105                        // plugin found ?
106                        if (localPlugin != null)
107                        {
108                            // launch it if actionable
109                            if (localPlugin.isActionable())
110                                PluginLauncher.start(localPlugin);
111                            else
112                            {
113                                // just display info
114                                ThreadUtil.invokeLater(new Runnable()
115                                {
116                                    @Override
117                                    public void run()
118                                    {
119                                        new PluginDetailPanel(localPlugin);
120                                    }
121                                });
122                            }
123                        }
124                    }
125                }
126            });
127        }
128    }
129
130    private static final String ID_SEARCH_RESULT = "searchresult";
131    private static final String ID_PLUGIN = "plugin";
132    private static final String ID_CLASSNAME = "classname";
133    // private static final String ID_NAME = "name";
134    private static final String ID_TEXT = "string";
135
136    @Override
137    public int getOrder()
138    {
139        // should be right after local plugin
140        return 6;
141    }
142
143    @Override
144    public String getName()
145    {
146        return "Online plugins";
147    }
148
149    @Override
150    public String getTooltipText()
151    {
152        return "Result(s) from online plugin";
153    }
154
155    @Override
156    public void doSearch(Document doc, String text, SearchResultConsumer consumer)
157    {
158        // Online plugin loader failed --> exit
159        if (!ensureOnlineLoaderLoaded())
160            return;
161
162        // no need to spent more time here...
163        if (hasWaitingSearch())
164            return;
165
166        // get online plugins
167        final List<PluginDescriptor> onlinePlugins = PluginRepositoryLoader.getPlugins();
168        // get online result node
169        final Element resultElement = XMLUtil.getElement(doc.getDocumentElement(), ID_SEARCH_RESULT);
170
171        if (resultElement == null)
172            return;
173
174        // get the local plugin search provider from search engine
175        final SearchEngine se = Icy.getMainInterface().getSearchEngine();
176        LocalPluginSearchResultProducer lpsrp = null;
177
178        if (se != null)
179        {
180            for (SearchResultProducer srp : se.getSearchResultProducers())
181                if (srp instanceof LocalPluginSearchResultProducer)
182                    lpsrp = (LocalPluginSearchResultProducer) srp;
183        }
184
185        final List<SearchWord> words = PluginSearchResultProducerHelper.getSearchWords(text);
186        final List<SearchResult> tmpResults = new ArrayList<SearchResult>();
187
188        for (Element plugin : XMLUtil.getElements(resultElement, ID_PLUGIN))
189        {
190            // abort
191            if (hasWaitingSearch())
192                return;
193
194            final SearchResult result = getResult(consumer, onlinePlugins, plugin, words, lpsrp);
195
196            if (result != null)
197                tmpResults.add(result);
198        }
199
200        // use a copy to avoid future concurrent accesses
201        results = new ArrayList<SearchResult>(tmpResults);
202        consumer.resultsChanged(this);
203
204        // load descriptions
205        for (SearchResult result : tmpResults)
206        {
207            // abort
208            if (hasWaitingSearch())
209                return;
210
211            ((OnlinePluginResult) result).getPlugin().loadDescriptor();
212            consumer.resultChanged(this, result);
213        }
214
215        // load images
216        for (SearchResult result : tmpResults)
217        {
218            // abort
219            if (hasWaitingSearch())
220                return;
221
222            ((OnlinePluginResult) result).getPlugin().loadImages();
223            consumer.resultChanged(this, result);
224        }
225    }
226
227    private static boolean ensureOnlineLoaderLoaded()
228    {
229        PluginRepositoryLoader.waitLoaded();
230
231        // repository loader failed --> retry once
232        if (PluginRepositoryLoader.failed() && NetworkUtil.hasInternetAccess())
233        {
234            PluginRepositoryLoader.reload();
235            PluginRepositoryLoader.waitLoaded();
236        }
237
238        return !PluginRepositoryLoader.failed();
239    }
240
241    private OnlinePluginResult getResult(SearchResultConsumer consumer, List<PluginDescriptor> onlinePlugins,
242            Element pluginNode, List<SearchWord> words, LocalPluginSearchResultProducer lpsrp)
243    {
244        final String className = XMLUtil.getElementValue(pluginNode, ID_CLASSNAME, "");
245        final String text = XMLUtil.getElementValue(pluginNode, ID_TEXT, "");
246        int priority;
247
248        final PluginDescriptor localPlugin = PluginLoader.getPlugin(className);
249        // exists in local ?
250        if (localPlugin != null)
251        {
252            // if we have the local search provider, we try to add result if not already existing
253            if (lpsrp != null)
254            {
255                final List<SearchResult> localResults = lpsrp.getResults();
256                boolean alreadyExists = false;
257
258                synchronized (localResults)
259                {
260                    for (SearchResult result : localResults)
261                    {
262                        if (((LocalPluginResult) result).getPlugin() == localPlugin)
263                        {
264                            alreadyExists = true;
265                            break;
266                        }
267                    }
268                }
269
270                // not already present in local result --> add it
271                if (!alreadyExists)
272                {
273                    priority = PluginSearchResultProducerHelper.searchInPlugin(localPlugin, words);
274
275                    // not found in local description --> assume low priority
276                    if (priority == 0)
277                        priority = 1;
278
279                    lpsrp.addResult(new LocalPluginResult(lpsrp, localPlugin, text, words, priority), consumer);
280                }
281            }
282
283            // don't return it for online result
284            return null;
285        }
286
287        final PluginDescriptor onlinePlugin = PluginDescriptor.getPlugin(onlinePlugins, className);
288        // cannot be found in online ? --> no result
289        if (onlinePlugin == null)
290            return null;
291
292        // try to get priority on result
293        onlinePlugin.loadDescriptor();
294        priority = PluginSearchResultProducerHelper.searchInPlugin(onlinePlugin, words);
295
296        // only keep high priority info from local data
297        if (priority <= 5)
298            priority = 1;
299
300        return new OnlinePluginResult(this, onlinePlugin, text, words, priority);
301    }
302}