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}