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) 2012 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.data; 040 041import java.io.Serializable; 042import java.net.MalformedURLException; 043import java.net.URL; 044 045import org.dcm4che3.data.Tag; 046 047/** 048 * @author Gunter Zeilinger <gunterze@gmail.com> 049 */ 050public class Code implements Serializable { 051 052 private static final String NO_CODE_MEANING = "<none>"; 053 054 private static final long serialVersionUID = 8807594793107889446L; 055 056 public enum CodeValueType { SHORT, LONG, URN }; 057 058 private String codeValue; 059 private String codingSchemeDesignator; 060 private String codingSchemeVersion; 061 private String codeMeaning; 062 private CodeValueType codeValueType; 063 064 public Code(String codeValue, String codingSchemeDesignator, 065 String codingSchemeVersion, String codeMeaning) { 066 this(codeValue, codingSchemeDesignator, codingSchemeVersion, codeMeaning, 067 guessCodeValueType(codeValue)); 068 } 069 070 public Code(String codeValue, String codingSchemeDesignator, 071 String codingSchemeVersion, String codeMeaning, CodeValueType codeValueType) { 072 if (codeValue == null) 073 throw new NullPointerException("Missing Code Value"); 074 if (codeValueType == null) 075 throw new NullPointerException("Missing Code Value Type"); 076 this.codeValue = codeValue; 077 init(codingSchemeDesignator, codingSchemeVersion, codeMeaning); 078 this.codeValueType = codeValueType; 079 } 080 081 public Code(String s) { 082 int len = s.length(); 083 if (len < 9 084 || s.charAt(0) != '(' 085 || s.charAt(len-2) != '"' 086 || s.charAt(len-1) != ')') 087 throw new IllegalArgumentException(s); 088 089 int endVal = s.indexOf(','); 090 int endScheme = s.indexOf(',', endVal + 1); 091 int startMeaning = s.indexOf('"', endScheme + 1) + 1; 092 this.codeValue = trimsubstring(s, 1, endVal); 093 this.codingSchemeDesignator = trimsubstring(s, endVal+1, endScheme); 094 this.codeMeaning = trimsubstring(s, startMeaning, len-2); 095 if (codingSchemeDesignator.endsWith("]")) { 096 int endVersion = s.lastIndexOf(']', endScheme - 1); 097 endScheme = s.lastIndexOf('[', endVersion - 1); 098 this.codingSchemeDesignator = trimsubstring(s, endVal+1, endScheme); 099 this.codingSchemeVersion = trimsubstring(s, endScheme+1, endVersion); 100 } 101 codeValueType = guessCodeValueType(codeValue); 102 } 103 104 private String trimsubstring(String s, int start, int end) { 105 try { 106 String trim = s.substring(start, end).trim(); 107 if (!trim.isEmpty()) 108 return trim; 109 } catch (StringIndexOutOfBoundsException e) {} 110 throw new IllegalArgumentException(s); 111 } 112 113 public Code(Attributes item) { 114 init(item.getString(Tag.CodingSchemeDesignator, null), item.getString(Tag.CodingSchemeVersion, null), 115 item.getString(Tag.CodeMeaning, NO_CODE_MEANING)); 116 codeValue = item.getString(Tag.CodeValue, null); 117 if (codeValue == null) { 118 codeValue = item.getString(Tag.LongCodeValue, null); 119 if (codeValue == null) { 120 codeValue = item.getString(Tag.URNCodeValue, null); 121 if (codeValue == null) { 122 throw new NullPointerException("Missing Code Value"); 123 } 124 codeValueType = CodeValueType.URN; 125 } else { 126 codeValueType = CodeValueType.LONG; 127 } 128 } else { 129 codeValueType = Code.CodeValueType.SHORT; 130 } 131 } 132 133 protected Code() {} // needed for JPA 134 135 public String getCodeValue() { 136 return codeValue; 137 } 138 139 public CodeValueType getCodeValueType() { 140 if (codeValueType == null) { 141 codeValueType = guessCodeValueType(codeValue); 142 } 143 return codeValueType; 144 } 145 146 public String getCodingSchemeDesignator() { 147 return codingSchemeDesignator; 148 } 149 150 public String getCodingSchemeVersion() { 151 return codingSchemeVersion; 152 } 153 154 public String getCodeMeaning() { 155 return codeMeaning; 156 } 157 158 @Override 159 public int hashCode() { 160 return 37 * (37 * (37 * 161 codeValue.hashCode() + 162 codeMeaning.hashCode()) + 163 codingSchemeDesignator.hashCode()) + 164 hashCode(codingSchemeVersion); 165 } 166 167 private int hashCode(String s) { 168 return s == null ? 0 : s.hashCode(); 169 } 170 171 @Override 172 public boolean equals(Object o) { 173 return equals(o, false); 174 } 175 176 public boolean equalsIgnoreMeaning(Code o) { 177 return equals(o, true); 178 } 179 180 private boolean equals(Object o, boolean ignoreMeaning) { 181 if (o == this) 182 return true; 183 if (!(o instanceof Code)) 184 return false; 185 Code other = (Code) o; 186 return codeValue.equals(other.codeValue) 187 && codingSchemeDesignator.equals(other.codingSchemeDesignator) 188 && equals(codingSchemeVersion, other.codingSchemeVersion) 189 && (ignoreMeaning || codeMeaning.equals(other.codeMeaning)); 190 } 191 192 private boolean equals(String s1, String s2) { 193 return s1 == s2 || s1 != null && s1.equals(s2); 194 } 195 196 @Override 197 public String toString() { 198 StringBuilder sb = new StringBuilder(); 199 sb.append('(').append(codeValue).append(", ").append(codingSchemeDesignator); 200 if (codingSchemeVersion != null) 201 sb.append(" [").append(codingSchemeVersion).append(']'); 202 sb.append(", \"").append(codeMeaning).append("\")"); 203 return sb.toString(); 204 } 205 206 public Attributes toItem() { 207 Attributes codeItem = new Attributes(codingSchemeVersion != null ? 4 : 3); 208 switch (getCodeValueType()) { 209 case SHORT: 210 codeItem.setString(Tag.CodeValue, VR.SH, codeValue); 211 break; 212 case LONG: 213 codeItem.setString(Tag.LongCodeValue, VR.UC, codeValue); 214 break; 215 case URN: 216 codeItem.setString(Tag.URNCodeValue, VR.UR, codeValue); 217 } 218 219 codeItem.setString(Tag.CodingSchemeDesignator, VR.SH, codingSchemeDesignator); 220 if (codingSchemeVersion != null) 221 codeItem.setString(Tag.CodingSchemeVersion, VR.SH, codingSchemeVersion); 222 codeItem.setString(Tag.CodeMeaning, VR.LO, codeMeaning); 223 return codeItem ; 224 } 225 226 private void init(String codingSchemeDesignator, String codingSchemeVersion, String codeMeaning) { 227 if (codingSchemeDesignator == null) 228 throw new NullPointerException("Missing Coding Scheme Designator"); 229 if (codeMeaning == null) 230 throw new NullPointerException("Missing Code Meaning"); 231 this.codingSchemeDesignator = codingSchemeDesignator; 232 this.codingSchemeVersion = codingSchemeVersion; 233 this.codeMeaning = codeMeaning; 234 } 235 236 private static CodeValueType guessCodeValueType(String codeValue) { 237 return codeValue.length() <= 16 ? CodeValueType.SHORT : 238 isURN(codeValue) ? CodeValueType.URN : CodeValueType.LONG; 239 } 240 241 private static boolean isURN(String codeValue) { 242 if (codeValue.indexOf(':') == -1) { 243 return false; 244 } 245 if (codeValue.startsWith("urn:")) { 246 return true; 247 } 248 try { 249 new URL(codeValue); 250 return true; 251 } catch (MalformedURLException e) { 252 return false; 253 } 254 } 255}