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.server.web.dicom;
020
021import java.io.IOException;
022import java.io.StringWriter;
023import java.net.URI;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.concurrent.CountDownLatch;
027import java.util.concurrent.ExecutionException;
028
029import org.jdom2.Document;
030import org.jdom2.Element;
031import org.jdom2.output.Format;
032import org.jdom2.output.XMLOutputter;
033
034import com.google.common.base.CharMatcher;
035import java.util.List;
036
037import pt.ua.dicoogle.plugins.PluginController;
038import pt.ua.dicoogle.sdk.StorageInputStream;
039import pt.ua.dicoogle.sdk.StorageInterface;
040import pt.ua.dicoogle.sdk.utils.DictionaryAccess;
041import pt.ua.dicoogle.sdk.datastructs.SearchResult;
042import pt.ua.dicoogle.sdk.task.JointQueryTask;
043import pt.ua.dicoogle.sdk.task.Task;
044
045/**
046 * Provides several helper functions for retrieving information about a DICOM file.
047 *
048 * @author António Novo <antonio.novo@ua.pt>
049 * @author Eduardo Pinho <eduardopinho@ua.pt>
050 */
051public class Information
052{
053        /**
054         * Based on a SOP Instance UID, returns a File handler for the respective .dcm file. This method
055     * issues all available query providers, see {@link Information#getFileFromSOPInstanceUID(java.lang.String, java.util.List)}
056     * to select specific providers
057         *
058         * @param sopInstanceUID a String containing a valid/indexed SOP Instance UID.
059         * @return a File handler for the respective .dcm file if the SOP Instance UID is valid and indexed, null otherwise.
060         */
061        public static StorageInputStream getFileFromSOPInstanceUID(String sopInstanceUID)
062        {
063        return getFileFromSOPInstanceUID(sopInstanceUID, null);
064        }
065
066    /**
067         * Based on a SOP Instance UID, returns a Dicoogle storage file handle for the respective resource file.
068         *
069         * @param sopInstanceUID a String containing a valid/indexed SOP Instance UID.
070     * @param providers a list of query sources to issue the file handler (if null, all enabled providers are queried)
071         * @return a File handler for the respective .dcm file if the SOP Instance UID is valid and indexed, null otherwise.
072         */
073        public static StorageInputStream getFileFromSOPInstanceUID(String sopInstanceUID, List<String> providers)
074        {
075        System.err.printf("getFileFromSOPInstanceUID(%s, %s)\n", sopInstanceUID, providers);
076                if (sopInstanceUID == null)
077                        return null;
078        
079        if (providers == null) {
080            providers = PluginController.getInstance().getQueryProvidersName(true);
081        }
082
083                String query = "SOPInstanceUID:" + sopInstanceUID;
084
085                CountDownLatch latch = new CountDownLatch(1);   
086                MyHolder holder= new MyHolder(latch);
087                PluginController.getInstance().query(holder, providers, query, new HashMap<String, String>());
088
089                try {
090                        latch.await();
091                } catch (InterruptedException e1) {
092                        // TODO Auto-generated catch block
093                        e1.printStackTrace();
094                }
095                
096                try {
097                        latch.await();
098                } catch (InterruptedException e) {
099                        // TODO Auto-generated catch block
100                        e.printStackTrace();
101                }
102                
103        return holder.getRet();
104        }
105        
106        private static class MyHolder extends JointQueryTask{
107                private StorageInputStream ret = null;
108                private CountDownLatch latch;           
109                
110                @Override
111                public void onReceive(Task<Iterable<SearchResult>> e) {
112                        try {           
113                                URI uri= null;
114                                 Iterable<SearchResult> itResults = e.get();
115                                 
116                                 for(SearchResult r : itResults){
117                                        if(uri == null)
118                                                uri = r.getURI();
119                                    System.out.println("URI: "+uri.toString());
120                                }
121                                
122                                if(uri != null){
123                                        StorageInterface str = PluginController.getInstance().getStorageForSchema(uri);
124                                    if(str != null){
125                                        Iterable<StorageInputStream> stream = str.at(uri);
126                                        for( StorageInputStream r : stream){
127                                                ret = r;
128                                                
129                                                stopAllTaks();
130                                                
131                                                latch.countDown();
132                                                
133                                                return;
134                                            }
135                                    }
136                                }
137                        } catch (InterruptedException | ExecutionException e1) {
138                                // TODO Auto-generated catch block
139                                e1.printStackTrace();
140                        }
141                }
142                
143                public MyHolder(CountDownLatch latch) {
144                        super();
145                        this.latch = latch;
146                }
147
148                private void stopAllTaks() {
149                        this.cancel(true);
150                }
151
152                @Override
153                public void onCompletion() {
154                        latch.countDown();
155                }
156
157                public StorageInputStream getRet() {
158                        return ret;
159                }
160        }
161        /**
162         * Based on a SOP Instance UID returns a hash table containing all name and value tag pairs for the respective .dcm file.
163     * This method issues all available query providers, see {@link Information#getFileFromSOPInstanceUID(java.lang.String, java.util.List)}
164     * to select specific providers.
165         *
166         * @param sopInstanceUID a String containing a valid/indexed SOP Instance UID.
167         * @return a Hashtables containing all name and value tag pairs for the respective .dcm file if the SOP Instance UID is valid and indexed, null otherwise.
168         */
169        public static HashMap<String, Object> searchForFileIndexedMetaData(String sopInstanceUID)
170        {
171        return searchForFileIndexedMetaData(sopInstanceUID, null);
172    }
173
174        /**
175         * Based on a SOP Instance UID returns a hash table containing all name and value tag pairs for the respective .dcm file.
176         *
177         * @param sopInstanceUID a String containing a valid/indexed SOP Instance UID.
178     * @param providers a list of query sources to issue the tables
179         * @return a Hashtables containing all name and value tag pairs for the respective .dcm file if the SOP Instance UID is valid and indexed, null otherwise.
180         */
181        public static HashMap<String, Object> searchForFileIndexedMetaData(String sopInstanceUID, List<String> providers) // XXX SOP Instance UID should always be set within a DICOM file, I think...
182        {
183                if (sopInstanceUID == null)
184                        return null;
185        
186        if (providers == null) {
187            providers = PluginController.getInstance().getQueryProvidersName(true);
188        }
189
190                // add all those tags to the extra fields that will be retried on a search query
191                HashMap<String, String> extraFields = new HashMap<>();
192                // get all the tags that can possibly be used within the file
193                HashMap<String, Integer> allTags = DictionaryAccess.getInstance().getTagList();
194
195                for(String key : allTags.keySet()){
196                        extraFields.put(key, null);
197                }
198                /*
199                HashMap<Integer, TagValue> mf = TagsStruct.getInstance().getManualFields();
200                for (Integer i : mf.keySet())
201                {
202                        extraFields.put(mf.get(i).getAlias(), null);
203                }
204                HashMap<Integer, TagValue> df = TagsStruct.getInstance().getDimFields();
205                for (Integer i : df.keySet())
206                {
207                        extraFields.put(df.get(i).getAlias(), null);
208                }*/
209
210                // build the query setring for the search
211                String query = "SOPInstanceUID:" + sopInstanceUID;
212
213                //execute search
214        Iterable<SearchResult> itResults = null;
215                try {
216                        
217                        JointQueryTask holder = new JointQueryTask() {
218                                
219                                @Override
220                                public void onReceive(Task<Iterable<SearchResult>> e) {
221                                        // TODO Auto-generated method stub
222                                        
223                                }
224                                
225                                @Override
226                                public void onCompletion() {
227                                        // TODO Auto-generated method stub
228                                        
229                                }
230                        };
231                        itResults = PluginController.getInstance().query(holder, providers, query, extraFields).get();
232                } catch (InterruptedException e) {
233                        // TODO Auto-generated catch block
234                        e.printStackTrace();
235                } catch (ExecutionException e) {
236                        // TODO Auto-generated catch block
237                        e.printStackTrace();
238                }
239                
240                if(itResults == null)
241                        return null;
242                
243                HashMap<String, Object> ret = new HashMap<>();
244                // return the first result (which should be the only one, by the way) extra fields (which contains all the tags and their values for this file)
245        for(SearchResult r : itResults){
246            ret.putAll(r.getExtraData());
247        }
248        return ret;//no results
249    }
250
251        private static final char start = 0;
252        private static final char end = 31;
253
254        /**
255         * Based on a SOP Instance UID returns a String containing a XML document filled with all name and value tag pairs for the respective .dcm file.
256         * This method will query all enabled sources.
257     * 
258         * @param sopInstanceUID a String containing a valid/indexed SOP Instance UID.
259         * @return a String containing a XML document filled with all name and value tag pairs for the respective .dcm file if the SOP Instance UID is valid and indexed, null otherwise.
260         */
261        public static String getXMLTagListFromFile(String sopInstanceUID) {
262        return getXMLTagListFromFile(sopInstanceUID, null);
263    }
264
265    /**
266         * Based on a SOP Instance UID returns a String containing a XML document filled with all name and value tag pairs for the respective .dcm file.
267         *
268         * @param sopInstanceUID a String containing a valid/indexed SOP Instance UID.
269     * @param providers a list of query sources to issue the document
270         * @return a String containing a XML document filled with all name and value tag pairs for the respective .dcm file if the SOP Instance UID is valid and indexed, null otherwise.
271         */
272        public static String getXMLTagListFromFile(String sopInstanceUID, List<String> providers)
273        {
274                // get all the tags and their values present on the file
275                HashMap<String, Object> tags = searchForFileIndexedMetaData(sopInstanceUID, providers);
276                if (tags == null)
277                {
278                        return null;
279                }
280
281                // create the XML string builder and open the xml document
282                StringBuilder xml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
283                xml.append("<tags>");
284                
285                Element rootElem = new Element("tags");
286                
287                // loop through all the tags set and add them and their values to the XML tree
288                Iterator<String> it = tags.keySet().iterator();
289                while (it.hasNext())
290                {
291                        String key = it.next();
292                        String value = (String) tags.get(key);
293                        value = value.trim();
294                        
295                        value = CharMatcher.inRange(start, end).and(CharMatcher.noneOf("\t\r\n")).collapseFrom(value, ' ');
296                        
297                        Element tagElem = new Element("tag");
298                        tagElem.setAttribute("name", key);
299                        tagElem.setText(value);
300                        
301                        rootElem.addContent(tagElem);
302                }
303                
304                XMLOutputter outStream = new XMLOutputter(Format.getCompactFormat());
305                StringWriter wr = new StringWriter();
306                try {
307                        outStream.output(new Document(rootElem), wr);
308                } catch (IOException e) {
309                        return null;
310                }
311                
312                return wr.toString();
313        }
314
315        public static float getFrameRateFromImage(String sopUID){
316                HashMap<String, Object> tags = searchForFileIndexedMetaData(sopUID);
317                if(tags == null)
318                        return 0;
319
320                for(String key : tags.keySet()){
321                        System.out.println("Key: "+key+" Value: "+tags.get(key));
322                }
323                
324                if(tags.containsKey("RecommendedDisplayFrameRateInFloat"))
325                        return Float.parseFloat("RecommendedDisplayFrameRateInFloat");
326                
327                if(tags.containsKey("RecommendedDisplayFrameRate"))
328                        return Float.parseFloat("RecommendedDisplayFrameRate");
329                
330                return 0;
331        }
332        
333        public static int getNumberOfFramesInFile(String sopInstanceUID){
334                StorageInputStream dcmFile = Information.getFileFromSOPInstanceUID(sopInstanceUID);
335        
336                if(dcmFile == null)
337                        return -1;
338                
339                return Convert2PNG.getNumberOfFrames(dcmFile);
340        }
341}