/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is part of dcm4che, an implementation of DICOM(TM) in * Java(TM), hosted at https://github.com/gunterze/dcm4che. * * The Initial Developer of the Original Code is * Agfa Healthcare. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * See @authors listed below * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.dcm4chee.archive.entity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.util.Arrays; import java.util.Date; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.SpecificCharacterSet; import org.dcm4che3.data.Tag; import org.dcm4che3.data.UID; import org.dcm4che3.data.VR; import org.dcm4che3.io.DicomInputStream; import org.dcm4che3.io.DicomOutputStream; import org.dcm4che3.util.StringUtils; import org.dcm4che3.util.TagUtils; import org.dcm4chee.archive.conf.AttributeFilter; import org.dcm4chee.archive.conf.MetadataUpdateStrategy; import org.dcm4chee.archive.conf.PrivateTag; import org.dcm4chee.storage.conf.Availability; /** * @author Gunter Zeilinger <gunterze@gmail.com> */ public class Utils { public static String upper (String value) { if (value == null) return null; return value.toUpperCase(); } public static byte[] encodeAttributes(Attributes attrs) { ByteArrayOutputStream out = new ByteArrayOutputStream(512); try { @SuppressWarnings("resource") DicomOutputStream dos = new DicomOutputStream(out, UID.ExplicitVRLittleEndian); dos.writeDataset(null, attrs); } catch (IOException e) { throw new RuntimeException(e); } return out.toByteArray(); } /** * */ public static String digestAttributes(Attributes attrs, MessageDigest digest) throws IOException { OutputStream nulloutputstream = new OutputStream() { /** Discards the specified byte. */ @Override public void write(int b) { } /** Discards the specified byte array. */ @Override public void write(byte[] b, int off, int len) { }}; if (digest != null) { digest.reset(); nulloutputstream = new DigestOutputStream(nulloutputstream, digest); } DicomOutputStream dout = new DicomOutputStream(nulloutputstream, UID.ExplicitVRLittleEndian); dout.writeDataset(null, attrs); dout.flush(); dout.close(); return TagUtils.toHexString(digest.digest()); } public static Attributes decodeAttributes(byte[] b) { if (b == null || b.length == 0) return new Attributes(0); ByteArrayInputStream is = new ByteArrayInputStream(b); try { @SuppressWarnings("resource") DicomInputStream dis = new DicomInputStream(is); return dis.readDataset(-1, -1); } catch (IOException e) { throw new BlobCorruptedException(e); } } public static void decodeAttributes(Attributes attrs, byte[] b) { if (b == null || b.length == 0) return; ByteArrayInputStream is = new ByteArrayInputStream(b); try { @SuppressWarnings("resource") DicomInputStream dis = new DicomInputStream(is); dis.readFileMetaInformation(); dis.readAttributes(attrs, -1, -1); } catch (IOException e) { throw new BlobCorruptedException(e); } } public static void setStudyQueryAttributes(Attributes attrs, int numberOfStudyRelatedSeries, int numberOfStudyRelatedInstances, String modalitiesInStudy, String sopClassesInStudy, int numberVisibleInstances, PrivateTag numberVisibleInstancesTag, Date lastUpdateTime, PrivateTag lastUpdateTimeTag) { attrs.setInt(Tag.NumberOfStudyRelatedSeries, VR.IS, numberOfStudyRelatedSeries); attrs.setInt(Tag.NumberOfStudyRelatedInstances, VR.IS, numberOfStudyRelatedInstances); attrs.setString(Tag.ModalitiesInStudy, VR.CS, StringUtils.split(modalitiesInStudy, '\\')); attrs.setString(Tag.SOPClassesInStudy, VR.CS, StringUtils.split(sopClassesInStudy, '\\')); if (lastUpdateTimeTag!=null && lastUpdateTime!=null) attrs.setDate(lastUpdateTimeTag.getCreator(), lastUpdateTimeTag.getIntTag(),VR.DT,lastUpdateTime); if (numberVisibleInstancesTag!=null) attrs.setInt(numberVisibleInstancesTag.getCreator(), numberVisibleInstancesTag.getIntTag(), VR.IS, numberVisibleInstances); } public static void setSeriesQueryAttributes(Attributes attrs, int numberOfSeriesRelatedInstances, int numberVisibleInstances, PrivateTag numberVisibleInstancesTag, Date lastUpdateTime, PrivateTag lastUpdateTimeTag) { attrs.setInt(Tag.NumberOfSeriesRelatedInstances, VR.IS, numberOfSeriesRelatedInstances); if (numberVisibleInstancesTag!=null) attrs.setInt(numberVisibleInstancesTag.getCreator(), numberVisibleInstancesTag.getIntTag(), VR.IS, numberVisibleInstances); if (lastUpdateTimeTag!=null && lastUpdateTime!=null) attrs.setDate(lastUpdateTimeTag.getCreator(), lastUpdateTimeTag.getIntTag(), VR.DT, lastUpdateTime); } public static String[] decodeAETs(String aetsSeparated) { return StringUtils.split(aetsSeparated, '\\'); } public static void setRetrieveAET(Attributes attrs, String retrieveAETs) { if (retrieveAETs != null) attrs.setString(Tag.RetrieveAETitle, VR.AE, StringUtils.split(retrieveAETs, '\\')); } public static void setRetrieveAET(Attributes attrs, String[] retrieveAETs) { if (retrieveAETs != null) attrs.setString(Tag.RetrieveAETitle, VR.AE, retrieveAETs); } public static void setAvailability(Attributes attrs, Availability availability) { attrs.setString(Tag.InstanceAvailability, VR.CS, availability.toString()); } public static String[] intersection(String[] ss1, String[] ss2) { int l = 0; for (int i = 0; i < ss1.length; i++) if (contains(ss2, ss1[i])) ss1[l++] = ss1[i]; return l == ss1.length ? ss1 : Arrays.copyOf(ss1, l); } public static boolean contains(String[] ss, String s0) { for (String s : ss) if (s0.equals(s)) return true; return false; } public static Attributes mergeAndNormalize(Attributes... attrsList) { SpecificCharacterSet globalCs = null; for (Attributes attrs : attrsList) { SpecificCharacterSet cs = attrs.getSpecificCharacterSet(); if (globalCs == null) { globalCs = cs; // first } else { if (!cs.equals(globalCs)) { if (!(globalCs.containsASCII() && cs.isASCII())) { if (globalCs.isUTF8()) { // convert to UTF8 convertToUTF8(attrsList); break; } else if (cs.isUTF8()) { globalCs = cs; convertToUTF8(attrsList); break; } else if (globalCs.isASCII() && cs.containsASCII()) { // do not decode globalCs = cs; } else { // incompatible codes, set all to UTF-8 globalCs = SpecificCharacterSet .valueOf("ISO_IR 192"); // UTF-8 convertToUTF8(attrsList); break; } } } } } // merge Attributes mergedAttrs = new Attributes(); for (Attributes attrs : attrsList) mergedAttrs.addAll(attrs); mergedAttrs.setString(Tag.SpecificCharacterSet, VR.CS, globalCs.toCodes()); return mergedAttrs; } private static void convertToUTF8(Attributes... attrsList) { for (Attributes attrs : attrsList) { if (!attrs.getSpecificCharacterSet().isUTF8()) attrs.setSpecificCharacterSet("ISO_IR 192"); // UTF-8 } } /** * Updates an attributes set (the current one) with a set of incoming * attributes, according to the configured strategy. Eventually stores * in a modified set all the current modified attributes. A default * value for the update strategy is also passed to the method * * @return <tt>true</tt> if one ore more attribute of the "current" set * were added or overwritten with a different value */ public static boolean updateAttributes (Attributes current, Attributes incoming, Attributes modified, AttributeFilter filter, MetadataUpdateStrategy defaultStrategy) { MetadataUpdateStrategy strategy = filter.getMetadataUpdateStrategy() != null ? filter .getMetadataUpdateStrategy() : defaultStrategy; switch (strategy) { case COERCE: return false; case COERCE_MERGE: return current.mergeSelected(incoming, filter.getCompleteSelection(incoming)); case OVERWRITE: return current.addSelected(incoming, filter.getCompleteSelection(incoming)); case OVERWRITE_MERGE: return current.updateSelected(incoming, modified, filter.getCompleteSelection(incoming)); } return false; } }