Commits

Anonymous committed 7427c46

added function hashUID

  • Participants
  • Parent commits ccb2246
  • Tags v2.2.0

Comments (0)

Files changed (5)

   <groupId>org.nrg</groupId>
   <artifactId>DicomEdit</artifactId>
   <packaging>jar</packaging>
-  <version>2.1.1</version>
+  <version>2.2.0</version>
   <name>DicomEdit</name>
   <description>Language for modifying DICOM metadata</description>
   <url>http://nrg.wustl.edu</url>
     	<artifactId>DicomUtils</artifactId>
     	<version>1.2.0</version>
     </dependency>
+    <dependency>
+    	<groupId>com.fasterxml.uuid</groupId>
+    	<artifactId>java-uuid-generator</artifactId>
+    	<version>3.1.3</version>
+    </dependency>
   </dependencies>
 </project>

File src/main/java/org/nrg/dcm/edit/ScriptApplicator.java

 import org.nrg.dcm.DicomUtils;
 import org.nrg.dcm.edit.fn.Format;
 import org.nrg.dcm.edit.fn.GetURL;
+import org.nrg.dcm.edit.fn.HashUID;
 import org.nrg.dcm.edit.fn.Lowercase;
 import org.nrg.dcm.edit.fn.Match;
 import org.nrg.dcm.edit.fn.Replace;
                     astParser.setFunction(Replace.name, new Replace());
                     astParser.setFunction(Match.name, new Match());
                     astParser.setFunction(UrlEncode.name, new UrlEncode());
+                    astParser.setFunction(HashUID.name, new HashUID());
                     for (final Map.Entry<String,ScriptFunction> me : functions.entrySet()) {
                         logger.trace("adding function {}", me);
                         astParser.setFunction(me.getKey(), me.getValue());
         return getSortedVariables(Arrays.asList(excluding));
     }
 
-    private static final List<String> NO_STRINGS = Collections.emptyList();
-
     public List<Variable> getSortedVariables() {
-        return this.getSortedVariables(NO_STRINGS);
+        return this.getSortedVariables(Collections.<String>emptyList());
     }
 
     public List<Statement> getStatements() {

File src/main/java/org/nrg/dcm/edit/fn/HashUID.java

+/**
+ * Copyright (c) 2012 Washington University
+ */
+package org.nrg.dcm.edit.fn;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.UUID;
+
+import org.dcm4che2.data.DicomObject;
+import org.nrg.dcm.edit.ScriptEvaluationException;
+import org.nrg.dcm.edit.ScriptFunction;
+import org.nrg.dcm.edit.Value;
+import org.nrg.dcm.edit.Variable;
+
+import com.fasterxml.uuid.Generators;
+
+/**
+ * Creates a one-way hash UID by first creating a Version 5 UUID (SHA-1 hash) from the provided string,
+ * then converting that UUID to a UID.
+ * Portions of this code are derived from dcm4che, an MPL 1.1-licensed open source project
+ * hosted at http://sourceforge.net/projects/dcm4che.
+ *
+ * @author Kevin A. Archie <karchie@wustl.edu>
+ * @author Gunter Zeilinger <gunterze@gmail.com> { original dcm4che implementation of UUID-to-UID conversion }
+ *
+ */
+public final class HashUID implements ScriptFunction {
+    public static final String name = "hashUID";
+
+    /**
+     * UID root for UUIDs (Universally Unique Identifiers) generated as per Rec. ITU-T X.667 | ISO/IEC 9834-8.
+     * @see <a href="http://www.oid-info.com/get/2.25">OID repository {joint-iso-itu-t(2) uuid(25)}</a>
+     */
+    public static final String UUID_ROOT = "2.25";
+
+    /* (non-Javadoc)
+     * @see org.nrg.dcm.edit.ScriptFunction#apply(java.util.List)
+     */
+    public Value apply(final List<Value> args) throws ScriptEvaluationException {
+        if (args.isEmpty()) {
+            throw new ScriptEvaluationException("usage: hashUID[string-to-hash {, optional algorithm-name} ]");
+        }
+        final Value sv = args.get(0);
+        return new Value() {
+            public Set<Variable> getVariables() {
+                return sv.getVariables();
+            }
+            
+            public SortedSet<Integer> getTags() {
+                return sv.getTags();
+            }
+            
+            public String on(final DicomObject o) throws ScriptEvaluationException {
+                try {
+                    return toUID(toUUID(sv.on(o)));
+                } catch (ScriptEvaluationException e) {
+                    throw e;
+                } catch (Throwable t) {
+                    throw new ScriptEvaluationException(t);
+                }
+            }
+
+            public String on(Map<Integer,String> m) throws ScriptEvaluationException {
+                try {
+                    return toUID(toUUID(sv.on(m)));
+                } catch (ScriptEvaluationException e) {
+                    throw e;
+                } catch (Throwable t) {
+                    throw new ScriptEvaluationException(t);
+                }
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * @see org.dcm4che2.util.UIDUtils#doCreateUID(java.util.String)
+     * @param uuid
+     * @return UID derived from the provided UUID
+     */
+    private static String toUID(final UUID uuid) {
+        final byte[] b17 = new byte[17];
+        fill(b17, 1, uuid.getMostSignificantBits());
+        fill(b17, 9, uuid.getLeastSignificantBits());
+        return new StringBuilder(64).append(UUID_ROOT).append('.')
+        .append(new BigInteger(b17)).toString();
+    }
+
+    /* (non-Javadoc)
+     * @see org.dcm4che2.util.UIDUtils#fill(byte[], int, long)
+     */
+    private static void fill(byte[] bb, int off, long val) {
+        for (int i = off, shift = 56; shift >= 0; i++, shift -= 8)
+            bb[i] = (byte) (val >>> shift);
+    }
+ 
+    /**
+     * Generates a Version 5 UUID from the provided string
+     * @param s source string
+     * @return Version 5 UUID
+     */
+    private static UUID toUUID(final String s) {
+        return Generators.nameBasedGenerator().generate(s.getBytes());
+    }
+}

File src/test/java/org/nrg/dcm/edit/ScriptApplicatorTest.java

 
 import org.dcm4che2.data.DicomObject;
 import org.dcm4che2.data.Tag;
+import org.dcm4che2.util.UIDUtils;
 import org.nrg.dcm.DicomUtils;
 
 import com.google.common.base.Function;
     private static final String S_USE_FORMAT = "(0008,103e) := format[\"{0}_{1}\", (0008,103e), \"extended\"]\n";
 
     private static final String S_USE_REPLACE = "(0008,103e) := replace[(0008,103e), \"_\", \":\"]\n";
+    
+    private static final String S_USE_HASHUID = "(0020,000d) := hashUID[(0020,000d)]\n";
 
     private static final String S_DESCRIPTION = "describe foo \"Description\"\nbar := foo\n";
 
         };
         final DicomObject do4 = loader.apply(f4);
         //	final DicomObject do5 = (DicomObject)loader.apply(f5);
-        final DicomObject do6 = (DicomObject)loader.apply(f6);
+        final DicomObject do6 = loader.apply(f6);
 
         final ScriptApplicator s_assign = new ScriptApplicator(bytes(S_ASSIGN));
         assertEquals(S_ASSIGN_TAGS, s_assign.getTags());
         final ScriptApplicator s_use_replace = new ScriptApplicator(bytes(S_USE_REPLACE));
         final DicomObject do4_use_replace = s_use_replace.apply(f4);
         assertEquals("t1:mpr:1mm:p2:pos50", do4_use_replace.getString(Tag.SeriesDescription));
+        
+        final ScriptApplicator s_use_hashUID = new ScriptApplicator(bytes(S_USE_HASHUID));
+        final DicomObject do4_use_hashUID = s_use_hashUID.apply(f4);
+        assertTrue(UIDUtils.isValidUID(do4_use_hashUID.getString(Tag.StudyInstanceUID)));
+        assertFalse(do4.getString(Tag.StudyInstanceUID).equals(do4_use_hashUID.getString(Tag.StudyInstanceUID)));
     }
 
     public void testGetSortedVariables() throws Exception {

File src/test/java/org/nrg/dcm/edit/fn/HashUIDTest.java

+/**
+ * Copyright (c) 2012 Washington University
+ */
+package org.nrg.dcm.edit.fn;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.dcm4che2.data.DicomObject;
+import org.dcm4che2.data.Tag;
+import org.dcm4che2.data.UID;
+import org.dcm4che2.util.UIDUtils;
+import org.nrg.dcm.DicomUtils;
+import org.nrg.dcm.edit.ConstantValue;
+import org.nrg.dcm.edit.ScriptEvaluationException;
+import org.nrg.dcm.edit.SingleTagValue;
+import org.nrg.dcm.edit.Value;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Kevin A. Archie <karchie@wustl.edu>
+ *
+ */
+public class HashUIDTest extends TestCase {
+    private static final File sampleDir = new File(System.getProperty("sample.data.dir"));
+    private static final File f4 = new File(sampleDir, "1.MR.head_DHead.4.1.20061214.091206.156000.1632817982.dcm.gz");
+
+    public void testApplyValidity() throws ScriptEvaluationException {
+        final Value v = new ConstantValue("foo");
+        final Value uidv = new HashUID().apply(Arrays.asList(v));
+        final String uid = uidv.on(Collections.<Integer,String>emptyMap());
+        assertTrue(UIDUtils.isValidUID(uid));
+        assertTrue(uid.startsWith("2.25."));
+    }
+
+    public void testApplyReproducibility() throws ScriptEvaluationException {
+        final Value v1 = new ConstantValue("bar");
+        final Value v2 = new ConstantValue("bar");
+        final Value uidv1 = new HashUID().apply(Arrays.asList(v1));
+        final Value uidv2 = new HashUID().apply(Arrays.asList(v2));
+        final String uid1 = uidv1.on(Collections.<Integer,String>emptyMap());
+        final String uid2 = uidv2.on(Collections.<Integer,String>emptyMap());
+        assertEquals(uid1, uid2);
+    }
+
+    public void testApplyUniqueness() throws ScriptEvaluationException {
+        final List<? extends Value> vs = Lists.newArrayList(new ConstantValue(UID.MRImageStorage),
+                new ConstantValue(UID.CTImageStorage),
+                new ConstantValue(UID.ExplicitVRBigEndian),
+                new ConstantValue(UID.JPEG2000));
+        final List<String> uids = Lists.transform(vs, new Function<Value,String>() {
+            public String apply(final Value v) {
+                try {
+                    return new HashUID().apply(Arrays.asList(v)).on(Collections.<Integer,String>emptyMap());
+                } catch (ScriptEvaluationException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        assertEquals(vs.size(), uids.size());
+        assertEquals(uids.size(), Sets.newLinkedHashSet(uids).size());
+    }
+    
+    public void testApplyToEmptyString() throws ScriptEvaluationException {
+        final Value v = new ConstantValue("");
+        final Value uidv = new HashUID().apply(Arrays.asList(v));
+        final String uid = uidv.on(Collections.<Integer,String>emptyMap());
+        assertTrue(UIDUtils.isValidUID(uid));
+        assertTrue(uid.startsWith("2.25."));
+    }
+    
+    public void testApplyToDicomObject() throws IOException,ScriptEvaluationException {
+        final DicomObject o = DicomUtils.read(f4);
+        final Value v1 = new SingleTagValue(Tag.StudyInstanceUID);
+        final Value v2 = new SingleTagValue(Tag.SeriesInstanceUID);
+        final Value v3 = new SingleTagValue(Tag.SOPInstanceUID);
+        final Value uidv1 = new HashUID().apply(Arrays.asList(v1));
+        final Value uidv2 = new HashUID().apply(Arrays.asList(v2));
+        final Value uidv3 = new HashUID().apply(Arrays.asList(v3));
+        final String uid1 = uidv1.on(o);
+        final String uid2 = uidv2.on(o);
+        final String uid3 = uidv3.on(o);
+        assertTrue(UIDUtils.isValidUID(uid1));
+        assertTrue(UIDUtils.isValidUID(uid2));
+        assertTrue(UIDUtils.isValidUID(uid3));
+        assertFalse(uid1.equals(uid2));
+        assertFalse(uid2.equals(uid3));
+        assertFalse(uid3.equals(uid1));
+    }
+}