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) 2013 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 ***** */ 038 039package org.dcm4che3.imageio.codec; 040 041import org.dcm4che3.conf.core.api.ConfigurableClass; 042import org.dcm4che3.conf.core.api.ConfigurableProperty; 043import org.dcm4che3.conf.core.api.LDAP; 044import org.dcm4che3.data.UID; 045import org.dcm4che3.imageio.codec.jpeg.PatchJPEGLS; 046import org.dcm4che3.util.ResourceLocator; 047import org.dcm4che3.util.SafeClose; 048import org.dcm4che3.util.StringUtils; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import javax.imageio.ImageIO; 053import javax.imageio.ImageReader; 054import java.io.File; 055import java.io.IOException; 056import java.io.InputStream; 057import java.io.Serializable; 058import java.net.MalformedURLException; 059import java.net.URL; 060import java.util.Iterator; 061import java.util.Map; 062import java.util.Map.Entry; 063import java.util.Properties; 064import java.util.TreeMap; 065 066/** 067 * Provides Image Readers for different DICOM transfer syntaxes and MIME types. 068 * 069 * @author Gunter Zeilinger <gunterze@gmail.com> 070 * @author Hermann Czedik-Eysenberg <hermann-agfa@czedik.net> 071 */ 072@LDAP(objectClasses = "dcmImageReaderFactory") 073@ConfigurableClass 074public class ImageReaderFactory implements Serializable { 075 076 private static final Logger LOG = LoggerFactory.getLogger(ImageReaderFactory.class); 077 078 private static final long serialVersionUID = -2881173333124498212L; 079 080 @LDAP(objectClasses = "dcmImageReader") 081 @ConfigurableClass 082 public static class ImageReaderParam implements Serializable { 083 084 private static final long serialVersionUID = 6593724836340684578L; 085 086 @ConfigurableProperty(name = "dcmIIOFormatName") 087 public String formatName; 088 089 @ConfigurableProperty(name = "dcmJavaClassName") 090 public String className; 091 092 @ConfigurableProperty(name = "dcmPatchJPEGLS") 093 public PatchJPEGLS patchJPEGLS; 094 095 public ImageReaderParam() { 096 } 097 098 public ImageReaderParam(String formatName, String className, 099 String patchJPEGLS) { 100 this.formatName = formatName; 101 this.className = nullify(className); 102 this.patchJPEGLS = patchJPEGLS != null && !patchJPEGLS.isEmpty() ? PatchJPEGLS 103 .valueOf(patchJPEGLS) : null; 104 } 105 106 public String getFormatName() { 107 return formatName; 108 } 109 110 public void setFormatName(String formatName) { 111 this.formatName = formatName; 112 } 113 114 public String getClassName() { 115 return className; 116 } 117 118 public void setClassName(String className) { 119 this.className = className; 120 } 121 122 public PatchJPEGLS getPatchJPEGLS() { 123 return patchJPEGLS; 124 } 125 126 public void setPatchJPEGLS(PatchJPEGLS patchJPEGLS) { 127 this.patchJPEGLS = patchJPEGLS; 128 } 129 } 130 131 private static String nullify(String s) { 132 return s == null || s.isEmpty() || s.equals("*") ? null : s; 133 } 134 135 private static ImageReaderFactory defaultFactory; 136 137 @LDAP(distinguishingField = "dicomTransferSyntax", noContainerNode = true) 138 @ConfigurableProperty( 139 name="dicomImageReaderMap", 140 label = "Image Readers by Transfer Syntax", 141 description = "Image readers by Transfer Syntax" 142 ) 143 private Map<String, ImageReaderParam> mapTransferSyntaxUIDs = new TreeMap<String, ImageReaderParam>(); 144 145 @ConfigurableProperty( 146 name="dicomImageReaderMapMime", 147 label = "Image Readers by MIME type", 148 description = "Image readers by MIME type" 149 ) 150 private Map<String, ImageReaderParam> mapMimeTypes = new TreeMap<String, ImageReaderParam>(); 151 152 public Map<String, ImageReaderParam> getMapTransferSyntaxUIDs() { 153 return mapTransferSyntaxUIDs; 154 } 155 156 public void setMapTransferSyntaxUIDs(Map<String, ImageReaderParam> mapTransferSyntaxUIDs) { 157 this.mapTransferSyntaxUIDs = mapTransferSyntaxUIDs; 158 } 159 160 public Map<String, ImageReaderParam> getMapMimeTypes() { 161 return mapMimeTypes; 162 } 163 164 public void setMapMimeTypes(Map<String, ImageReaderParam> mapMimeTypes) { 165 this.mapMimeTypes = mapMimeTypes; 166 } 167 168 public static ImageReaderFactory getDefault() { 169 if (defaultFactory == null) 170 defaultFactory = initDefault(); 171 172 return defaultFactory; 173 } 174 175 public static void resetDefault() { 176 defaultFactory = null; 177 } 178 179 public static void setDefault(ImageReaderFactory factory) { 180 if (factory == null) 181 throw new NullPointerException(); 182 183 defaultFactory = factory; 184 } 185 186 private static ImageReaderFactory initDefault() { 187 ImageReaderFactory factory = new ImageReaderFactory(); 188 String name = System.getProperty(ImageReaderFactory.class.getName(), 189 "org/dcm4che3/imageio/codec/ImageReaderFactory.properties"); 190 try { 191 factory.load(name); 192 } catch (Exception e) { 193 throw new RuntimeException( 194 "Failed to load Image Reader Factory configuration from: " 195 + name, e); 196 } 197 198 factory.init(); 199 200 return factory; 201 } 202 203 public void init() { 204 if (LOG.isDebugEnabled()) { 205 StringBuilder sb = new StringBuilder(); 206 sb.append("Image Readers:\n"); 207 for (Entry<String, ImageReaderParam> entry : mapTransferSyntaxUIDs.entrySet()) { 208 String tsUid = entry.getKey(); 209 sb.append(' ').append(tsUid); 210 sb.append(" (").append(UID.nameOf(tsUid)).append("): "); 211 sb.append(getImageReaderName(entry.getValue())).append('\n'); 212 } 213 for (Entry<String, ImageReaderParam> entry : mapMimeTypes.entrySet()) { 214 sb.append(' ').append(entry.getKey()).append(": "); 215 sb.append(getImageReaderName(entry.getValue())).append('\n'); 216 } 217 LOG.debug(sb.toString()); 218 } 219 } 220 221 private String getImageReaderName(ImageReaderParam imageReaderParam) { 222 ImageReader imageReader = null; 223 try { 224 imageReader = getImageReader(imageReaderParam); 225 } catch (RuntimeException e) { 226 // none found 227 } 228 return imageReader != null ? imageReader.getClass().getName() : "null"; 229 } 230 231 public void load(String name) throws IOException { 232 URL url; 233 try { 234 url = new URL(name); 235 } catch (MalformedURLException e) { 236 url = ResourceLocator.getResourceURL(name, this.getClass()); 237 if (url == null) { 238 File f = new File(name); 239 if(f.exists() && f.isFile()) { 240 url = f.toURI().toURL(); 241 } else { 242 throw new IOException("No such resource: " + name); 243 } 244 } 245 } 246 InputStream in = url.openStream(); 247 try { 248 load(in); 249 } finally { 250 SafeClose.close(in); 251 } 252 } 253 254 public void load(InputStream in) throws IOException { 255 Properties props = new Properties(); 256 props.load(in); 257 for (Map.Entry<Object, Object> entry : props.entrySet()) { 258 String key = (String) entry.getKey(); 259 260 String[] ss = StringUtils.split((String) entry.getValue(), ':'); 261 String formatName = ss[0]; 262 String className = ss[1]; 263 String patchJPEGLS = ss[2]; 264 265 if (key.contains("/")) { // mime type 266 mapMimeTypes.put(key, new ImageReaderParam(formatName, className, patchJPEGLS)); 267 } else { // transfer syntax uid 268 mapTransferSyntaxUIDs.put(key, new ImageReaderParam(formatName, className, patchJPEGLS)); 269 } 270 } 271 } 272 273 private ImageReaderParam getForTransferSyntaxUID(String tsuid) { 274 return mapTransferSyntaxUIDs.get(tsuid); 275 } 276 277 private ImageReaderParam getForMimeType(String mimeType) { 278 return mapMimeTypes.get(mimeType); 279 } 280 281 private boolean containsTransferSyntaxUID(String tsuid) { 282 return mapTransferSyntaxUIDs.containsKey(tsuid); 283 } 284 285 public static ImageReaderParam getImageReaderParam(String tsuid) { 286 return getDefault().getForTransferSyntaxUID(tsuid); 287 } 288 289 public static boolean canDecompress(String tsuid) { 290 return getDefault().containsTransferSyntaxUID(tsuid); 291 } 292 293 public static ImageReader getImageReader(ImageReaderParam param) { 294 295 Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(param.formatName); 296 297 while (readers.hasNext()) { 298 ImageReader reader = readers.next(); 299 300 if (param.className == null || param.className.equals(reader.getClass().getName())) { 301 LOG.debug("Using Image Reader {}", reader.getClass()); 302 return reader; 303 } 304 } 305 306 throw new RuntimeException("No matching Image Reader for format: " + param.formatName + " (Class: " + ((param.className == null) ? "*" : param.className) + ") registered"); 307 } 308 309 public static ImageReader getImageReaderForMimeType(String mimeType) { 310 ImageReaderParam imageReaderParam = getDefault().getForMimeType(mimeType); 311 312 if (imageReaderParam != null) { 313 // configured mime type 314 return getImageReader(imageReaderParam); 315 } else { 316 // not configured mime type, fallback to first ImageIO reader for this mime type 317 ImageReader reader = ImageIO.getImageReadersByMIMEType(mimeType).next(); 318 LOG.debug("Using Image Reader {}", reader.getClass()); 319 return reader; 320 } 321 } 322 323}