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-sdk.
005 *
006 * Dicoogle/dicoogle-sdk 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-sdk 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.sdk.utils;
020
021
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Set;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032import org.apache.commons.collections4.BidiMap;
033import org.apache.commons.collections4.SetUtils;
034import org.apache.commons.collections4.bidimap.DualHashBidiMap;
035
036/**
037 * Structure to manage all the tags inside Dicoogle.
038 * 
039 * There are three groups. DIM Fields, DICOM Fields and PrivateFields
040 * DIM Fields may either be DICOM or Private.
041 * A DICOM and Private groups do not overlap
042 * 
043 * Other fields is the common designation for fields that do not belong to the DIM.
044 *
045 * @author Luís A. Bastião Silva <bastiao@ua.pt>
046 * @author Tiago Marques Godinho <tmgodinho@ua.pt> Refactor
047 */
048public class TagsStruct
049{
050        private static final Logger logger = LoggerFactory.getLogger(TagsStruct.class);
051
052        //Optimization @TODO
053        //Map Structures
054        private BidiMap<Integer, String> tagNameMappings;       
055        private HashMap<Integer, TagValue> tagValueMappings;
056        //Lists (DIM, DICOM, PrivateFields)
057        private Set<TagValue> nDIMFields;
058        private Set<TagValue> nDICOMFields;
059        private Set<TagValue> nPrivateFields;
060        //Modalities
061        private Set<String> modalitiesSet;
062        //Index All Modalities
063    private boolean indexAllModalities = false;
064    //DeepSearch modalities
065    private boolean deepSearchModalities = true;
066    
067    private List<String> dictionaries = new ArrayList<>();
068
069    private static TagsStruct instance = null ;
070
071    public static synchronized TagsStruct getInstance()
072    {
073        if (instance == null) {
074            instance = new TagsStruct();
075        }
076        return instance;
077    }
078
079
080    private TagsStruct()
081    {
082        //Initialize fields;
083        this.tagNameMappings = new DualHashBidiMap<>();
084        this.tagValueMappings = new HashMap<>();
085        this.nDICOMFields = new HashSet<>();
086        this.nDIMFields = new HashSet<>();
087        this.nPrivateFields = new HashSet<>();
088        this.modalitiesSet = new HashSet<>();
089        
090        Map<String, Integer> tmp = DictionaryAccess.getInstance().getTagList();
091        for(Entry<String, Integer> e : tmp.entrySet()){
092                addDICOMField(new TagValue(e.getValue(), e.getKey()));
093        }       
094        
095        addDIMField(new TagValue(Integer.parseInt("1021c0", 16), "PregnancyStatus"));
096        addDIMField(new TagValue(
097                Integer.parseInt("81050", 16),"PerformingPhysicianName"));
098        addDIMField(new TagValue(
099                Integer.parseInt("400243", 16),"PerformedLocation"));
100        addDIMField(new TagValue(
101                Integer.parseInt("100020", 16),"PatientID"));
102        addDIMField(new TagValue(
103                Integer.parseInt("80080", 16),"InstitutionName"));
104        addDIMField(new TagValue(
105                Integer.parseInt("80050", 16),"AccessionNumber"));
106        addDIMField(new TagValue(
107                Integer.parseInt("80020", 16),"StudyDate"));
108        addDIMField(new TagValue(
109                Integer.parseInt("102154", 16),"PatientTelephoneNumbers"));
110        addDIMField(new TagValue(
111                Integer.parseInt("101010", 16),"PatientAge"));
112        addDIMField(new TagValue(
113                Integer.parseInt("81070", 16),"OperatorName"));
114        addDIMField(new TagValue(
115                Integer.parseInt("200010", 16),"StudyID"));
116        addDIMField(new TagValue(
117                Integer.parseInt("81040", 16),"InstitutionDepartmentName"));
118        addDIMField(new TagValue(
119                Integer.parseInt("20000e", 16),"SeriesInstanceUID"));
120        addDIMField(new TagValue(
121                Integer.parseInt("20000d", 16),"StudyInstanceUID"));
122        addDIMField(new TagValue(
123                Integer.parseInt("100040", 16),"PatientSex"));
124        addDIMField(new TagValue(
125                Integer.parseInt("201208", 16),"NumberOfStudyRelatedInstances"));
126        addDIMField(new TagValue(
127                Integer.parseInt("100010", 16),"PatientName"));
128        addDIMField(new TagValue(
129                Integer.parseInt("80070", 16),"Manufacturer"));
130        addDIMField(new TagValue(
131                Integer.parseInt("81090", 16),"ManufacturerModelName"));
132        addDIMField(new TagValue(
133                Integer.parseInt("81030", 16),"StudyDescription"));
134        addDIMField(new TagValue(
135                Integer.parseInt("700", 16),"Priority"));
136        addDIMField(new TagValue(
137                Integer.parseInt("80090", 16),"ReferringPhysicianName"));
138        addDIMField(new TagValue(
139                Integer.parseInt("80061", 16),"ModalitiesInStudy"));
140        addDIMField(new TagValue(
141                Integer.parseInt("80060", 16),"Modality"));
142        addDIMField(new TagValue(
143                Integer.parseInt("80030", 16),"StudyTime"));
144        addDIMField(new TagValue(
145                Integer.parseInt("80018", 16),"SOPInstanceUID"));
146        addDIMField(new TagValue(
147                Integer.parseInt("80050", 16),"AccessionNumber"));
148        addDIMField(new TagValue(
149                Integer.parseInt("80070", 16),"Manufacturer"));
150        addDIMField(new TagValue(
151                Integer.parseInt("700", 16),"Priority"));
152        addDIMField(new TagValue(
153                 Integer.parseInt("200011", 16),"SeriesNumber"));
154        addDIMField(new TagValue(
155                Integer.parseInt("08103e", 16),"SeriesDescription"));
156        addDIMField(new TagValue(
157                Integer.parseInt("100030", 16),"PatientBirthDate"));
158        
159        addModality("XA");
160        addModality("CT");
161        addModality("US");
162        addModality("MG");
163        addModality("MR");
164    }
165    
166    /**
167     * Adds a new Tag to the structure.
168     * 
169     * @param field
170     */
171    private synchronized void addTag(TagValue field){           
172        TagValue tmp = this.tagValueMappings.get(field.getTagNumber());
173        if(tmp == null){
174                this.tagNameMappings.put(field.getTagNumber(), field.getName());
175                this.tagValueMappings.put(field.getTagNumber(), field);                 
176        }else{
177                //merge tag information
178                tmp.updateTagInformation(field);
179                this.tagNameMappings.put(tmp.getTagNumber(), tmp.getName());
180        }
181    }
182    
183    /**
184     * Removes a given Tag from the structure
185     * @param field
186     */
187    private synchronized void removeTag(TagValue field){        
188        this.tagNameMappings.remove(field.getTagNumber());
189        this.tagValueMappings.remove(field.getTagNumber());             
190    }
191    
192    /**
193     * Adds a DIM Field. If the field already exists its information is updated.
194     * @param field
195     */
196    public synchronized void addDIMField(TagValue field){
197        //this.dimFields.put(field.getTagNumber(), field);
198        this.nDIMFields.add(field);
199        if(!isDICOMField(field) && !isPrivateField(field))
200                addPrivateField(field);
201        addTag(field);
202    }
203    
204    /**
205     * Adds a private field. Checks if the field is already present in the DICOM fields.
206     * @param field
207     * @return
208     */
209    public synchronized boolean addPrivateField(TagValue field){
210        addTag(field);
211        if(this.nDICOMFields.contains(field)){
212                return true;
213        }       
214        this.nPrivateFields.add(field);
215        return true;
216    }
217    
218    /**
219     * Removes a Private Field. It is not possible to remove the field if it belongs to the DIM or to the DICOM group.
220     * @param tagNumber
221     * @return
222     */
223    public synchronized boolean removePrivateField(int tagNumber){
224        //this.dimFields.put(field.getTagNumber(), field);
225        TagValue tag = this.tagValueMappings.get(tagNumber);
226        if(this.nDICOMFields.contains(tag)){
227                return false;
228        }
229        
230        this.nPrivateFields.remove(tag);
231        this.nDIMFields.remove(tag);
232        removeTag(tag);
233        return true;
234    }
235    
236    /**
237     * Adds a DICOM Field.
238     * @param field
239     */
240    private synchronized void addDICOMField(TagValue field){
241        //this.dimFields.put(field.getTagNumber(), field);
242        this.nDICOMFields.add(field);
243        addTag(field);
244    }    
245        
246    /**
247     * Adds a new Modality.
248     * @param modalityName
249     */
250    public void addModality(String modalityName){
251        this.modalitiesSet.add(modalityName);                   
252    }
253    
254    /**
255     * Removes All modalities.
256     */
257    public void removeAllModalities(){
258        this.modalitiesSet = new HashSet<>();
259    }
260    
261    /**
262     * Removes the specified modality.
263     * @param modality
264     * @return true if the modality was present in the structure.
265     */
266    public boolean removeModality(String modality){
267        return this.modalitiesSet.remove(modality);
268    }
269    
270    /**
271     * Gets a read-only view of the DIM Fields
272     * @return
273     */
274    public Set<TagValue> getDIMFields(){
275        return SetUtils.unmodifiableSet(this.nDIMFields);
276    }
277    
278    /**
279     * Gets a List with the DIM fields names.
280     * @return
281     */
282    public ArrayList<String> getDIMTagNames()
283    {
284        ArrayList<String> tagNames = new ArrayList<>(this.nDIMFields.size());
285        for(TagValue tag : this.nDIMFields){
286                tagNames.add(tag.getName());
287        }
288        return tagNames;
289    }
290    
291    /**
292     * Gets a List with the DIM fields alias.
293     * @return
294     */
295    public ArrayList<String> getDIMAlias()
296    {
297        ArrayList<String> alias = new ArrayList<>(this.nDIMFields.size());
298        for(TagValue tag : this.nDIMFields){
299                alias.add(tag.getAlias());
300        }
301        return alias;
302    }
303    
304    /**
305     * @return a Read-only view of all fields.
306     */
307    public Set<TagValue> getAllFields(){
308        return SetUtils.unmodifiableSet( new HashSet<>(tagValueMappings.values()) );
309    }
310
311    /**
312     * @return a Read only view of the Private Fields
313     */
314    public Set<TagValue> getPrivateFields(){
315        return SetUtils.unmodifiableSet(nPrivateFields);
316    }
317    
318    /**
319     * @return the Fields which do not belong to the DIM. (DICOM+Private)
320     */
321    public Set<TagValue> getOtherFields(){
322        
323        HashSet<TagValue> tags = new HashSet<>(this.nDICOMFields.size()+this.nPrivateFields.size());
324        for(TagValue tag : this.tagValueMappings.values()){
325                if(!isDICOMField(tag))
326                        tags.add(tag);
327                
328        }
329        return tags;
330    }
331    
332    /**
333     * 
334     * @param tagNumber
335     * @return The TagValue object for the given Number, or null.
336     */
337    public TagValue getTagValue(int tagNumber){
338        return this.tagValueMappings.get(tagNumber);
339    }
340    
341    /**
342     * 
343     * @param tagName
344     * @return The TagValue object for the given its name, or null.
345     */
346    public TagValue getTagValue(String tagName){
347        Integer x = this.tagNameMappings.getKey(tagName);
348        if(x == null)
349                return null;
350        return this.getTagValue(x);
351    }
352    
353    /**
354     * @param tag
355     * @return Whether the given tag is a DIM Field or not.
356     */
357    public boolean isDIMField(TagValue tag){
358        return this.nDIMFields.contains(tag);
359    }
360    
361    /**
362     * @param tag
363     * @return Whether the given tag is a DICOM Field or not.
364     */
365    public boolean isDICOMField(TagValue tag){
366        return this.nDICOMFields.contains(tag);
367    }
368    
369    /**
370     * @param tag
371     * @return Whether the given tag is a Private Field or not.
372     */
373    public boolean isPrivateField(TagValue tag){
374        return this.nPrivateFields.contains(tag);
375    }
376    
377    /**
378     * @param tag
379     * @return Whether the given tag is an other field or not. (OtherFields = PrivateFields + DICOMFields)
380     */
381    public boolean isOtherField(TagValue tag){
382        return containsTag(tag.getTagNumber()) && !isDICOMField(tag);
383    }
384    
385    /**
386     * Checks if the tag is present in the structure.
387     * @param tag 
388     * @return 
389     */
390    public boolean containsTag(int tag){
391        return this.tagValueMappings.containsKey(tag);
392    }
393    
394    /**
395     * Checks if the given modality is present in the structure.
396     * @param modality
397     * @return
398     */
399    public boolean containsModality(String modality){
400        return this.modalitiesSet.contains(modality);
401    }
402
403    /**
404     * @return a Read-only view of the Modalities.
405     */
406    public Set<String> getModalities() {
407                return SetUtils.unmodifiableSet(modalitiesSet);
408        }
409    
410    /**
411     * Checks if the given modality is Enabled.
412     * @param modality
413     * @return
414     */
415    public boolean isModalityEnable(String modality)
416    {
417        return isIndexAllModalitiesEnabled() || this.containsModality(modality);
418    }
419
420    /**
421     * @return the indexAllModalities
422     */
423    public boolean isIndexAllModalitiesEnabled() {
424        return indexAllModalities;
425    }
426
427    /**
428     * @param indexAllModalities the indexAllModalities to set
429     */
430    public void enableIndexAllModalities(boolean indexAllModalities) {
431        this.indexAllModalities = indexAllModalities;
432    }
433    
434    public boolean isDeepSearchModalitiesEnabled() {
435                return deepSearchModalities;
436        }
437
438
439        public void enableDeepSearchModalities(boolean deepSearchModalities) {
440                this.deepSearchModalities = deepSearchModalities;
441        }
442
443
444        /**
445     * @return the dictionaries
446     */
447    public List<String> getDictionaries() {
448        return dictionaries;
449    }
450
451    /**
452     * @param dictionaries the dictionaries to set
453     */
454    public void setDictionaries(List<String> dictionaries) {
455        this.dictionaries = dictionaries;
456    }
457    
458    
459    public void addDicionary(String dic)
460    {
461        this.dictionaries.add(dic);
462    }
463    public void removeDicionary(String dic)
464    {
465        this.dictionaries.remove(dic);
466    }
467    
468    
469}