001/* ***** BEGIN LICENSE BLOCK ***** 002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 003 * 004 * The contents of this file are subject to the Mozilla Public License Version 005 * 1.1 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * http://www.mozilla.org/MPL/ 008 * 009 * Software distributed under the License is distributed on an "AS IS" basis, 010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 011 * for the specific language governing rights and limitations under the 012 * License. 013 * 014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 015 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 016 * 017 * The Initial Developer of the Original Code is 018 * Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2011 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * ***** END LICENSE BLOCK ***** */ 038package org.dcm4che3.tool.dcmdict; 039 040 041import java.lang.reflect.Field; 042 043import org.dcm4che3.data.UID; 044 045import java.util.ArrayList; 046import java.util.HashMap; 047import java.util.Iterator; 048import java.util.LinkedList; 049import java.util.List; 050import java.util.ResourceBundle; 051 052import org.apache.commons.cli.CommandLine; 053import org.apache.commons.cli.Options; 054import org.apache.commons.cli.ParseException; 055import org.dcm4che3.data.ElementDictionary; 056import org.dcm4che3.tool.common.CLIUtils; 057import org.slf4j.Logger; 058import org.slf4j.LoggerFactory; 059 060/** 061 * @author Hesham Elbadawi <bsdreko@gmail.com> 062 * 063 */ 064 065public class DcmDict{ 066 067 private static final Logger LOG = LoggerFactory.getLogger(DcmDict.class); 068 069 private static Options options; 070 071 private ElementDictionary dict; 072 073 private LinkedList<String> queryKeys = new LinkedList<String>(); 074 075 private ArrayList<String> matches = new ArrayList<String>(); 076 077 private ArrayList<String> suggestions = new ArrayList<String>(); 078 079 private HashMap<String, List<String>> camelCaseMap = new HashMap<String, List<String>>(); 080 081 private static ResourceBundle rb = ResourceBundle 082 .getBundle("org.dcm4che3.tool.dcmdict.messages"); 083 084 public DcmDict() {} 085 086 private static CommandLine parseComandLine(String[] args) 087 throws ParseException { 088 options = new Options(); 089 options.addOption(null, "private-creator", true, rb.getString("private-creator")); 090 CLIUtils.addCommonOptions(options); 091 return CLIUtils.parseComandLine(args,options, rb, DcmDict.class); 092 } 093 094 @SuppressWarnings("unchecked") 095 public static void main(String[] args) { 096 CommandLine cl = null; 097 try { 098 DcmDict main = new DcmDict(); 099 cl = parseComandLine(args); 100 101 if(cl.hasOption("private-creator")) { 102 String creator = cl.getOptionValue("private-creator"); 103 main.dict = ElementDictionary.getElementDictionary(creator); 104 } 105 else { 106 main.dict = ElementDictionary.getStandardElementDictionary(); 107 } 108 main.queryKeys = (LinkedList<String>) cl.getArgList(); 109 if(hasAbbreviation(main.queryKeys)) { 110 buildCamelCaseMap(main); 111 } 112 buildMatches(main); 113 if(!main.matches.isEmpty()) { 114 printMatches(main); 115 } 116 if(!main.queryKeys.isEmpty()) { 117 buildSuggestions(main); 118 System.out.println(hashtag()); 119 printSuggestions(main); 120 } 121 } catch (ParseException e) { 122 LOG.error("dcmdict\t" + e.getMessage()); 123 System.err.println(rb.getString("try")); 124 System.exit(2); 125 } 126 } 127 128 private static void buildCamelCaseMap(DcmDict main) { 129 for(Field field : main.dict.getTagClass().getFields()) { 130 if(main.camelCaseMap.get(getAbbreviation(getName(field.getName())))!=null) { 131 main.camelCaseMap.get(getAbbreviation(getName(field.getName()))).add(field.getName()); 132 } 133 else { 134 ArrayList<String> tmp = new ArrayList<String>(); 135 tmp.add(field.getName()); 136 main.camelCaseMap.put(getAbbreviation(getName(field.getName())), tmp); 137 } 138 } 139 for(Field field : UID.class.getFields()) { 140 if(main.camelCaseMap.get(getAbbreviation(getName(field.getName())))!=null) { 141 main.camelCaseMap.get(getAbbreviation(getName(field.getName()))).add(field.getName()); 142 } 143 else { 144 ArrayList<String> tmp = new ArrayList<String>(); 145 tmp.add(field.getName()); 146 main.camelCaseMap.put(getAbbreviation(getName(field.getName())), tmp); 147 } 148 } 149 } 150 151 private static boolean hasAbbreviation(LinkedList<String> queryKeys) { 152 for(String str : queryKeys) { 153 if(str.matches("[A-Z]+")) { 154 return true; 155 } 156 } 157 return false; 158 } 159 160 private static void buildSuggestions(DcmDict main) { 161 for(String key : main.queryKeys) { 162 if(isTag(key)) { 163 System.out.format("%-120s\n",hashtag()); 164 System.out.format("%-120s\n","Illegal argument -> "+key+" tags must be complete"); 165 } 166 else if(isUID(key)) { 167 System.out.format("%-120s\n",hashtag()); 168 System.out.format("%-120s\n","Illegal argument -> "+key+" uids must be complete"); 169 } 170 else { 171 if(key.matches("[A-Z]+")) { 172 for(String keyStr : main.camelCaseMap.keySet()) { 173 if(keyStr.startsWith(key)) { 174 for(String str : main.camelCaseMap.get(keyStr)) 175 main.suggestions.add(key + "\t"+str); 176 } 177 } 178 continue; 179 } 180 for(Field field : main.dict.getTagClass().getFields()) { 181 if(field.getName().toLowerCase().startsWith(key.toLowerCase())) 182 main.suggestions.add(key + "\t"+field.getName()); 183 } 184 for(Field field : UID.class.getFields()) { 185 if(field.getName().toLowerCase().startsWith(key.toLowerCase())) 186 main.suggestions.add(key + "\t"+field.getName()); 187 } 188 } 189 } 190 } 191 192 private static void buildMatches(DcmDict main) { 193 for(Iterator<String> iter = main.queryKeys.iterator(); iter.hasNext();) { 194 String key = iter.next(); 195 196 if(isTag(key)) { 197 key = key.replaceFirst("^0+(?!$)", ""); 198 int tagInDecimal = Integer.parseInt(key, 16); 199 String keyWord = main.dict.keywordOf(tagInDecimal); 200 String vr; 201 String name; 202 if(!keyWord.isEmpty()) { 203 vr = main.dict.vrOf(tagInDecimal).toString(); 204 name = getName(keyWord); 205 iter.remove(); 206 main.matches.add(name+"\t"+keyWord+"\t"+adjustHex(key)+"\t"+vr); 207 } 208 209 } 210 else if(isUID(key)) { 211 String name; 212 try{ 213 name = UID.nameOf(key); 214 if(!name.equalsIgnoreCase("?")) { 215 iter.remove(); 216 main.matches.add(name+"\t"+name.replaceAll("\\s+","")+"\t"+key+"\t"+"-"); 217 } 218 } 219 catch(IllegalArgumentException e) { 220 // 221 } 222 } 223 else { 224 if(key.matches("[A-Z]+")) { 225 if(main.camelCaseMap.get(key)!=null && main.camelCaseMap.get(key).size() == 1) 226 key = main.camelCaseMap.get(key).get(0); 227 } 228 int tag = main.dict.tagForKeyword(key); 229 String vr; 230 String name; 231 if(tag == -1) { 232 try{ 233 String uidStr = UID.forName(key); 234 name = UID.nameOf(uidStr); 235 iter.remove(); 236 main.matches.add(name+"\t"+key+"\t"+uidStr+"\t"+"-"); 237 } 238 catch(IllegalArgumentException e) { 239 // 240 } 241 } 242 else { 243 String hex = Long.toHexString(tag); 244 String tagStr = adjustHex(hex); 245 vr = main.dict.vrOf(tag).toString(); 246 name = getName(key); 247 iter.remove(); 248 main.matches.add(name+"\t"+key+"\t"+tagStr+"\t"+vr); 249 } 250 251 } 252 } 253 } 254 255 private static boolean isUID(String key) { 256 return key.matches("[012]((\\.0)|(\\.[1-9]\\d*))+"); 257 } 258 259 private static String adjustHex(String hex) { 260 String str = ""; 261 for(int i=8-hex.length();i>0;i--) { 262 str+="0"; 263 } 264 str+=hex; 265 return str.toUpperCase(); 266 } 267 268 private static String getName(String key) { 269 String name = ""; 270 for (String w : key.split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")) { 271 name+=w+" "; 272 } 273 return name.trim(); 274 } 275 276 private static String getAbbreviation(String name){ 277 String str = ""; 278 for(int i=0;i<name.split(" ").length;i++) { 279 str+=name.split(" ")[i].charAt(0); 280 } 281 return str.toUpperCase(); 282 } 283 private static boolean isTag(String key) { 284 if(key.matches("[0-9a-fA-F]+")) { 285 return true; 286 } 287 return false; 288 } 289 290 private static String hashtag() { 291 String out = ""; 292 for(int i=0;i<140;i++) { 293 out+="#"; 294 } 295 return out; 296 } 297 298 private static void printSuggestions(DcmDict main) { 299 String lastCurrent = null; 300 for(String str : main.suggestions) { 301 if(lastCurrent==null) { 302 lastCurrent= str.split("\t")[0]; 303 System.out.format("%-120s\n","Query Key ->"+str.split("\t")[0]+" can be :"); 304 } 305 if(!lastCurrent.equalsIgnoreCase(str.split("\t")[0])){ 306 System.out.format("%140s\n",hashtag()); 307 System.out.format("%-120s\n","Query Key ->"+str.split("\t")[0]+" can be :"); 308 lastCurrent= str.split("\t")[0]; 309 } 310 System.out.format("%-120s\n",str.split("\t")[1]); 311 } 312 } 313 314 private static void printMatches(DcmDict main) { 315 System.out.format("%140s\n",hashtag()); 316 System.out.format("%-50s%-50s%-30s%-10s\n","Name","Keyword","Tag/UID","VR"); 317 System.out.format("%140s\n",hashtag()); 318 for(String str : main.matches) 319 System.out.format("%-50s%-50s%-30s%-10s\n",str.split("\t")[0] 320 ,str.split("\t")[1],str.split("\t")[2],str.split("\t")[3]); 321 } 322 323}