001/**
002 * Copyright (C) 2014  Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
003 *
004 * This file is part of Dicoogle/dicoogle.
005 *
006 * Dicoogle/dicoogle 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 * Dicoogle/dicoogle 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 Dicoogle.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package pt.ua.dicoogle.rGUI.server.controllers;
020
021import java.io.File;
022import java.net.InetAddress;
023import java.net.URI;
024import java.rmi.RemoteException;
025import java.rmi.server.RemoteServer;
026import java.util.*;
027import java.util.AbstractMap.SimpleEntry;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033import pt.ua.dicoogle.core.QueryExpressionBuilder;
034import pt.ua.dicoogle.plugins.NetworkMember;
035import pt.ua.dicoogle.plugins.PluginController;
036import pt.ua.dicoogle.rGUI.RFileBrowser.RemoteFile;
037import pt.ua.dicoogle.rGUI.fileTransfer.FileSender;
038import pt.ua.dicoogle.rGUI.interfaces.controllers.ISearch;
039import pt.ua.dicoogle.rGUI.interfaces.signals.ISearchSignal;
040import pt.ua.dicoogle.rGUI.server.SearchHelper;
041import pt.ua.dicoogle.sdk.utils.DictionaryAccess;
042import pt.ua.dicoogle.sdk.Utils.TaskRequest;
043import pt.ua.dicoogle.sdk.Utils.TaskRequestsConstants;
044import pt.ua.dicoogle.sdk.datastructs.SearchResult;
045import pt.ua.dicoogle.sdk.observables.FileObservable;
046import pt.ua.dicoogle.sdk.observables.ListObservableSearch;
047import pt.ua.dicoogle.sdk.utils.TagValue;
048import pt.ua.dicoogle.sdk.utils.TagsStruct;
049
050/**
051 *  This class is the controller to search in IndexEngine or P2P Network
052 *
053 * @author Samuel Campos <samuelcampos@ua.pt>
054 */
055public class Search implements ISearch
056{
057
058    private ISearchSignal searchSignal;
059    private ArrayList<String> extrafields;
060    private ArrayList<SearchResult> resultList;
061    private ArrayList<SearchResult> P2PResultList;
062    private ArrayList<SearchResult> pendingP2PThumbnails;
063    private ArrayList<SearchResult> exportList;
064    private SearchHelper searchHelper;
065    private long searchTime;
066
067    public Search()
068    {
069        extrafields = new ArrayList<String>();
070
071        extrafields.add("PatientName");
072        extrafields.add("PatientID");
073        extrafields.add("Modality");
074        extrafields.add("StudyDate");
075        extrafields.add("SOPInstanceUID");
076        //extrafields.add("Thumbnail");
077
078        searchHelper = new SearchHelper(this);
079    }
080
081    public void setSearchTime(long time)
082    {
083        searchTime = time;
084
085        /*
086        try {
087        if(searchSignal != null)
088        searchSignal.sendSearchSignal(2);
089        } catch (RemoteException ex) {
090        searchSignal = null;
091        DebugManager.getInstance().debug("Failure to send the signal to the client..");
092        LoggerFactory.getLogger(Search.class).error(ex.getMessage(), ex);
093        }
094         * 
095         */
096    }
097
098    // SearchHelper sends the local searchResults
099    public void setLocalSearchResult(ArrayList<SearchResult> sResult)
100    {
101        this.resultList = sResult;
102
103        try
104        {
105            if (searchSignal != null)
106            {
107                searchSignal.sendSearchSignal(0);
108            }
109        } catch (RemoteException ex)
110        {
111            searchSignal = null;
112            //DebugManager.getInstance().debug("Failure to send the signal to the client..");
113            LoggerFactory.getLogger(Search.class).error(ex.getMessage(), ex);
114        }
115    }
116    
117    
118    public void queryFinished()
119    {
120
121        try
122        {
123            if (searchSignal != null)
124            {
125                searchSignal.sendSearchSignal(5);
126            }
127        } catch (RemoteException ex)
128        {
129            searchSignal = null;
130            //DebugManager.getInstance().debug("Failure to send the signal to the client..");
131            LoggerFactory.getLogger(Search.class).error(ex.getMessage(), ex);
132        }
133    }
134
135    // SearchHelper sends the new P2P searchResults
136    public void setP2PSearchResult(ArrayList<SearchResult> sResult)
137    {
138        //System.out.println("entrou no setP2PSearchResult com " + sResult.size() + " resultados");
139        this.P2PResultList = sResult;
140        
141        try
142        {
143            if (searchSignal != null)
144            {
145                //System.out.println("there is searchsignal");
146                searchSignal.sendSearchSignal(1);
147            }
148        } catch (RemoteException ex)
149        {
150            searchSignal = null;
151            //DebugManager.getInstance().debug("Failure to send the signal to the client..");
152            LoggerFactory.getLogger(Search.class).error(ex.getMessage(), ex);
153        }
154    }
155
156    public void setExportSearchResult(ArrayList<SearchResult> sResult)
157    {
158        if (this.exportList != null)
159        {
160            exportList.addAll(sResult);
161        }
162        else
163        {
164            this.exportList = sResult;
165        }
166
167        try
168        {
169            
170            if (searchSignal != null)
171            {
172                searchSignal.sendSearchSignal(4);
173            }
174        } catch (RemoteException ex)
175        {
176            searchSignal = null;
177            //DebugManager.getInstance().debug("Failure to send the signal to the client..");
178            LoggerFactory.getLogger(Search.class).error(ex.getMessage(), ex);
179        }
180        
181    }
182
183    public void setP2PThumbnails(ArrayList<SearchResult> sResult)
184    {
185        if (pendingP2PThumbnails != null)
186        {
187            pendingP2PThumbnails.addAll(sResult);
188        } else
189        {
190            pendingP2PThumbnails = sResult;
191        }
192
193        try
194        {
195            if (searchSignal != null)
196            {
197                searchSignal.sendSearchSignal(3);
198            }
199
200        } catch (RemoteException ex)
201        {
202            searchSignal = null;
203            //DebugManager.getInstance().debug("Failure to send the signal to the client..");
204            LoggerFactory.getLogger(Search.class).error(ex.getMessage(), ex);
205        }
206    }
207
208    
209    
210    @Override
211    public void Search(String query, boolean keywords, HashMap<String,Boolean> plugins) throws RemoteException
212
213    {
214        if (query.isEmpty())
215
216        {
217            query = "*:*";
218        } else
219        {
220            if (!keywords)
221            {
222                /**
223                 * Write the QueryString respecting BNF grammer
224                 * defined regarding Lucene documentation 2.4.X branch
225                 */
226                QueryExpressionBuilder _expression = new QueryExpressionBuilder(query);
227                query = _expression.getQueryString();
228            }
229            //DebugManager.getInstance().debug(">>> New Query String is:" + query);
230
231        }
232        searchTime = 0;
233        searchHelper.search(query, extrafields, plugins, false);
234    }
235
236    @Override
237    public ListObservableSearch<SearchResult> SearchToExport(String query, boolean keywords, HashMap<String, Boolean> plugins, ArrayList<String> eFields, Observer obs) throws RemoteException
238    {
239        if (query.isEmpty())
240        {
241            query = "*:*";
242        } else
243        {
244            if (!keywords)
245            {
246                QueryExpressionBuilder _expression = new QueryExpressionBuilder(query);
247                query = _expression.getQueryString();
248            }
249        }
250        
251        return searchHelper.search(query, eFields, plugins, true, obs);
252    
253    }
254
255    @Override
256    public void pruneQuery(String id) throws RemoteException
257    {
258        PluginController.getInstance().addTask(new TaskRequest(TaskRequestsConstants.T_QUERY_PRUNE, null, null));
259    }
260
261    class SearchDumper implements Observer
262    {
263
264        static final int WAIT_TIME = 10;
265        
266        ListObservableSearch<SearchResult> result = null;
267        
268        ArrayList<SearchResult> resultArr = new ArrayList<SearchResult>();
269        
270        public ArrayList<SearchResult> search(SearchResult r, String query, ArrayList<String> extrafields)
271        {
272                return null;
273            /*synchronized(this)
274            {
275                //TODO: DELETED
276                //result = PluginController.getInstance().searchOne(r.getURI().toString(),
277                //query , extrafields, r.getURI().toString(), this);
278                
279                
280                //System.out.println("Query: " + query);
281                //System.out.println("Plugin: " + r.getPluginName());
282                //System.out.println("Waiting for results");
283                resultArr.addAll(result.getArray());
284                try {
285                    this.wait(WAIT_TIME);
286                } catch (InterruptedException ex) {
287                    LoggerFactory.getLogger(Search.class).error(ex.getMessage(), ex);
288                }
289            }
290            //System.out.println("Received the results + "+resultArr.size());
291            //System.out.println("Received the results + "+result.getArray());
292            return resultArr;*/
293        }
294        
295        public List<SearchResult> getResults()
296        {
297            //System.out.println("Received the results2 + "+resultArr.size());
298            //System.out.println("Received the results2 + "+result.getArray());
299            return this.resultArr;
300        }
301        
302        @Override
303        public void update(Observable o, Object o1) 
304        {
305            //System.out.println("Update + "+o);
306            if (o1==null)
307            {
308                synchronized(this)
309                {
310                    this.notifyAll();
311                }
312                return;
313            }
314            ArrayList tmp = ((ListObservableSearch) o1).getArray();
315            //System.out.println("Result_: " + tmp);
316            //System.out.println("Received a result_");
317            synchronized(this)
318            {
319                resultArr.addAll(tmp);
320                this.notifyAll();
321            }
322            //System.out.println("Received a result - done notification_");
323   
324        }
325    }
326    
327    @Override
328    public ArrayList<SearchResult> SearchIndexedMetaData(SearchResult searchResult) throws RemoteException {
329        
330        TagsStruct tags = TagsStruct.getInstance();
331        
332        ArrayList<String> extraFields = new ArrayList<>();
333        
334        for(TagValue tag : tags.getAllFields())
335                extraFields.add(tag.getAlias());
336        
337        String query = "FileName:" + searchResult.getURI() + " AND FileHash:"+ searchResult.get("filehash");
338
339        
340        SearchDumper sdumper = new SearchDumper();
341        sdumper.search(searchResult, query, extraFields);
342        List<SearchResult> result = sdumper.getResults();
343        //System.out.println("Result + " +result);
344        if (result.size() > 0) {
345            
346            SearchResult r = (SearchResult) result.get(0);
347            for (String s:r.getExtraData().keySet())
348            {
349                if (s.contains("Sequence"))
350                {
351                    String [] fields = r.getExtraData().get(s).toString().split(" ");
352                    for (int i = 0 ; i<fields.length; i++)
353                    {
354                            extraFields.add(fields[i]);
355                    }
356                    
357                }
358            }
359            
360            sdumper = new SearchDumper();
361            sdumper.search(searchResult, query, extraFields);
362            result = sdumper.getResults();
363         
364        }
365        
366        return (ArrayList<SearchResult>) result;
367    }
368    
369    
370    
371
372    @Override
373    public long getSearchTime() throws RemoteException
374    {
375        return searchTime;
376    }
377
378    @Override
379    public void RegisterSignalBack(ISearchSignal signalBack) throws RemoteException
380    {
381        //System.out.println("Registring SignalBack");
382
383        //if(signalBack == null)
384        //    System.out.println("signalBack == null");
385        //else
386        //    System.out.println("signalBack != null");
387
388        searchSignal = signalBack;
389
390        //System.out.println("SignalBack Registered");
391    }
392
393    @Override
394    public FileObservable RequestP2PFile(SearchResult file) throws RemoteException
395    {
396        //TOSO: fix this
397       // return PluginController.getInstance().requestFile(file.getURI(), file.getURI(), file.getURI(), file.get("filehash"));
398        //PeerEngine.getInstance().requestFile(file.getAddress(), file.getFileName(), file.getFileHash());
399        return null;
400    }
401
402    /**
403     * Don't work with SearchResultP2P
404     * 
405     * @param file
406     * @return
407     * @throws RemoteException
408     */
409    @Override
410    public SimpleEntry<RemoteFile, Integer> downloadFile(SearchResult result) throws RemoteException
411    {
412        /*if (!PluginController.getInstance().isLocalPlugin(result.getPluginName()))
413        {
414            return null;
415        }*/
416        try
417        {
418            String path = result.getURI().toString().replace('\\', '/');
419
420            File file = new File(path);
421            InetAddress client = InetAddress.getByName(RemoteServer.getClientHost());
422
423            FileSender sender = new FileSender(file, client);
424
425            SimpleEntry<RemoteFile, Integer> entry = new SimpleEntry<RemoteFile, Integer>(new RemoteFile(file), sender.getListenerPort());
426
427            //DebugManager.getInstance().debug("Transfering file: " + entry.getKey().getName() + ", listening port: " + entry.getValue());
428
429            Thread tSender = sender;
430            tSender.start();
431
432            return entry;
433
434        } catch (Exception e)
435        {
436            e.printStackTrace();
437        }
438        return null;
439    }
440
441    @Override
442    public List<NetworkMember> getPeerList() throws RemoteException
443    {
444        //TODO: DELETED
445        //return PluginController.getInstance().getMembers();
446        return null;
447    }
448
449    @Override
450    public ArrayList<SearchResult> getSearchResults() throws RemoteException
451    {
452        ArrayList<SearchResult> returnResultList = resultList;
453
454        resultList = null;
455
456        return returnResultList;
457    }
458
459    @Override
460    public ArrayList<SearchResult> getP2PSearchResults() throws RemoteException
461    {
462        ArrayList<SearchResult> returnResultList = P2PResultList;
463
464        P2PResultList = null;
465
466        return returnResultList;
467    }
468
469    @Override
470    public ArrayList<SearchResult> getExportSearchResults() throws RemoteException
471    {
472        ArrayList<SearchResult> returnResultList = exportList;
473
474        exportList = null;
475
476        return returnResultList;
477    }
478
479    @Override
480    public SearchResult getThumbnail(URI FileName, String FileHash) throws RemoteException
481    {
482        return searchHelper.searchThumbnail(FileName, FileHash);
483    }
484
485    @Override
486    public void getP2PThumbnail(URI FileName, String FileHash, String addr) throws RemoteException
487    {
488        searchHelper.searchP2PThumbnail(FileName, FileHash, addr);
489    }
490
491    @Override
492    public ArrayList<SearchResult> getPendingP2PThumnails() throws RemoteException
493    {
494        ArrayList<SearchResult> returnResultList = pendingP2PThumbnails;
495
496        pendingP2PThumbnails = null;
497
498        return returnResultList;
499    }
500
501    @Override
502    public HashMap<String, Integer> getTagList() throws RemoteException
503    {
504        return DictionaryAccess.getInstance().getTagList();
505    }
506
507    @Override
508    public HashMap<Integer, TagValue> getDIMFields() throws RemoteException
509    {
510        Set<TagValue> fields = TagsStruct.getInstance().getDIMFields();
511        HashMap<Integer, TagValue> map = new HashMap<>(fields.size());
512        for(TagValue tag : fields)
513                map.put(tag.getTagNumber(), tag);
514        
515        return map;
516    }
517}