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}