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.util.ArrayList; 042import java.util.List; 043 044import org.dcm4che3.data.IOD.DataElement; 045import org.dcm4che3.util.ByteUtils; 046import org.dcm4che3.util.StringUtils; 047import org.dcm4che3.util.TagUtils; 048 049/** 050 * @author Gunter Zeilinger <gunterze@gmail.com> 051 * 052 */ 053public class ValidationResult { 054 055 public enum Invalid { 056 VR, 057 VM, 058 Value, 059 Item, 060 MultipleItems, 061 Code 062 } 063 064 public class InvalidAttributeValue { 065 public final IOD.DataElement dataElement; 066 public final Invalid reason; 067 public final ValidationResult[] itemValidationResults; 068 public final IOD[] missingItems; 069 public InvalidAttributeValue(DataElement dataElement, Invalid reason, 070 ValidationResult[] itemValidationResults, IOD[] missingItems) { 071 this.dataElement = dataElement; 072 this.reason = reason; 073 this.itemValidationResults = itemValidationResults; 074 this.missingItems = missingItems; 075 } 076 } 077 078 private ArrayList<IOD.DataElement> missingAttributes; 079 private ArrayList<IOD.DataElement> missingAttributeValues; 080 private ArrayList<IOD.DataElement> notAllowedAttributes; 081 private ArrayList<InvalidAttributeValue> invalidAttributeValues; 082 083 public boolean hasMissingAttributes() { 084 return missingAttributes != null; 085 } 086 087 public boolean hasMissingAttributeValues() { 088 return missingAttributeValues != null; 089 } 090 091 public boolean hasInvalidAttributeValues() { 092 return invalidAttributeValues != null; 093 } 094 095 public boolean hasNotAllowedAttributes() { 096 return notAllowedAttributes != null; 097 } 098 099 public boolean isValid() { 100 return !hasMissingAttributes() 101 && !hasMissingAttributeValues() 102 && !hasInvalidAttributeValues() 103 && !hasNotAllowedAttributes(); 104 } 105 106 public void addMissingAttribute(IOD.DataElement dataElement) { 107 if (missingAttributes == null) 108 missingAttributes = new ArrayList<IOD.DataElement>(); 109 missingAttributes.add(dataElement); 110 } 111 112 public void addMissingAttributeValue(IOD.DataElement dataElement) { 113 if (missingAttributeValues == null) 114 missingAttributeValues = new ArrayList<IOD.DataElement>(); 115 missingAttributeValues.add(dataElement); 116 } 117 118 public void addInvalidAttributeValue(IOD.DataElement dataElement, Invalid reason) { 119 addInvalidAttributeValue(dataElement, reason, null, null); 120 } 121 122 public void addInvalidAttributeValue(IOD.DataElement dataElement, 123 Invalid reason, ValidationResult[] itemValidationResult, IOD[] missingItems) { 124 if (invalidAttributeValues == null) 125 invalidAttributeValues = new ArrayList<InvalidAttributeValue>(); 126 invalidAttributeValues.add( 127 new InvalidAttributeValue(dataElement, reason, 128 itemValidationResult, missingItems)); 129 } 130 131 public void addNotAllowedAttribute(DataElement el) { 132 if (notAllowedAttributes == null) 133 notAllowedAttributes = new ArrayList<IOD.DataElement>(); 134 notAllowedAttributes.add(el); 135 } 136 137 public int[] tagsOfNotAllowedAttributes() { 138 return tagsOf(notAllowedAttributes); 139 } 140 141 public int[] tagsOfMissingAttributeValues() { 142 return tagsOf(missingAttributeValues); 143 } 144 145 public int[] tagsOfMissingAttributes() { 146 return tagsOf(missingAttributes); 147 } 148 149 public int[] tagsOfInvalidAttributeValues() { 150 ArrayList<InvalidAttributeValue> list = invalidAttributeValues; 151 if (list == null) 152 return ByteUtils.EMPTY_INTS; 153 154 int[] tags = new int[list.size()]; 155 for (int i = 0; i < tags.length; i++) 156 tags[i] = list.get(i).dataElement.tag; 157 return tags; 158 } 159 160 public int[] getOffendingElements() { 161 return cat(tagsOfMissingAttributes(), 162 tagsOfMissingAttributeValues(), 163 tagsOfInvalidAttributeValues(), 164 tagsOfNotAllowedAttributes()); 165 } 166 167 private int[] cat(int[]... iss) { 168 int length = 0; 169 for (int[] is : iss) 170 length += is.length; 171 int[] tags = new int[length]; 172 int off = 0; 173 for (int[] is : iss) { 174 System.arraycopy(is, 0, tags, off, is.length); 175 off += is.length; 176 } 177 return tags; 178 } 179 180 private int[] tagsOf(List<DataElement> list) { 181 if (list == null) 182 return ByteUtils.EMPTY_INTS; 183 184 int[] tags = new int[list.size()]; 185 for (int i = 0; i < tags.length; i++) 186 tags[i] = list.get(i).tag; 187 return tags; 188 } 189 190 public String getErrorComment() { 191 StringBuilder sb = new StringBuilder(); 192 if (notAllowedAttributes != null) 193 return errorComment(sb, "Not allowed Attribute", 194 tagsOfNotAllowedAttributes()).toString(); 195 if (missingAttributes != null) 196 return errorComment(sb, "Missing Attribute", 197 tagsOfMissingAttributes()).toString(); 198 if (missingAttributeValues != null) 199 return errorComment(sb, "Missing Value of Attribute", 200 tagsOfMissingAttributeValues()).toString(); 201 if (invalidAttributeValues != null) 202 return errorComment(sb, "Invalid Attribute", 203 tagsOfInvalidAttributeValues()).toString(); 204 return null; 205 } 206 207 private static StringBuilder errorComment(StringBuilder sb, String prompt, 208 int[] tags) { 209 sb.append(prompt); 210 String prefix = tags.length > 1 ? "s: " : ": "; 211 for (int tag : tags) { 212 sb.append(prefix).append(TagUtils.toString(tag)); 213 prefix = ", "; 214 } 215 return sb; 216 } 217 218 @Override 219 public String toString() { 220 if (isValid()) 221 return "VALID"; 222 223 StringBuilder sb = new StringBuilder(); 224 if (notAllowedAttributes != null) 225 errorComment(sb, "Not allowed Attribute", 226 tagsOfNotAllowedAttributes()).append(StringUtils.LINE_SEPARATOR); 227 if (missingAttributes != null) 228 errorComment(sb, "Missing Attribute", 229 tagsOfMissingAttributes()).append(StringUtils.LINE_SEPARATOR); 230 if (missingAttributeValues != null) 231 errorComment(sb, "Missing Value of Attribute", 232 tagsOfMissingAttributeValues()).append(StringUtils.LINE_SEPARATOR); 233 if (invalidAttributeValues != null) 234 errorComment(sb, "Invalid Attribute", 235 tagsOfInvalidAttributeValues()).append(StringUtils.LINE_SEPARATOR); 236 237 return sb.substring(0, sb.length()-1); 238 } 239 240 public String asText(Attributes attrs) { 241 if (isValid()) 242 return "VALID"; 243 244 StringBuilder sb = new StringBuilder(); 245 appendTextTo(0, attrs, sb); 246 return sb.substring(0, sb.length()-1); 247 } 248 249 private void appendTextTo(int level, Attributes attrs, StringBuilder sb) { 250 if (notAllowedAttributes != null) 251 appendTextTo(level, attrs, "Not allowed Attributes:", notAllowedAttributes, sb); 252 if (missingAttributes != null) 253 appendTextTo(level, attrs, "Missing Attributes:", missingAttributes, sb); 254 if (missingAttributeValues != null) 255 appendTextTo(level, attrs, "Missing Attribute Values:", missingAttributeValues, sb); 256 if (invalidAttributeValues != null) 257 appendInvalidAttributeValues(level, attrs, "Invalid Attribute Values:", sb); 258 } 259 260 private void appendTextTo(int level, Attributes attrs, String title, 261 List<DataElement> list, StringBuilder sb) { 262 appendPrefixTo(level, sb); 263 sb.append(title).append(StringUtils.LINE_SEPARATOR); 264 for (DataElement el : list) { 265 appendAttribute(level, el.tag, sb); 266 appendIODRef(el.getLineNumber(), sb); 267 sb.append(StringUtils.LINE_SEPARATOR); 268 } 269 } 270 271 private void appendIODRef(int lineNumber, StringBuilder sb) { 272 if (lineNumber > 0) 273 sb.append(" // IOD line #").append(lineNumber); 274 } 275 276 private void appendInvalidAttributeValues(int level, Attributes attrs, 277 String title, StringBuilder sb) { 278 appendPrefixTo(level, sb); 279 sb.append(title); 280 sb.append(StringUtils.LINE_SEPARATOR); 281 for (InvalidAttributeValue iav : invalidAttributeValues) { 282 int tag = iav.dataElement.tag; 283 appendAttribute(level, tag, sb); 284 VR.Holder vr = new VR.Holder(); 285 Object value = attrs.getValue(tag, vr); 286 sb.append(' ').append(vr.vr); 287 sb.append(" ["); 288 vr.vr.prompt(value, 289 attrs.bigEndian(), 290 attrs.getSpecificCharacterSet(vr.vr), 200, sb); 291 sb.append(']'); 292 if (iav.reason != Invalid.Item) { 293 sb.append(" Invalid ").append(iav.reason); 294 appendIODRef(iav.dataElement.getLineNumber(), sb); 295 } 296 sb.append(StringUtils.LINE_SEPARATOR); 297 if (iav.missingItems != null) { 298 for (IOD iod : iav.missingItems) { 299 appendPrefixTo(level+1, sb); 300 sb.append("Missing Item"); 301 appendIODRef(iod.getLineNumber(), sb); 302 sb.append(StringUtils.LINE_SEPARATOR); 303 } 304 } 305 if (iav.itemValidationResults != null) { 306 Sequence seq = (Sequence) value; 307 for (int i = 0; i < iav.itemValidationResults.length; i++) { 308 ValidationResult itemResult = iav.itemValidationResults[i]; 309 if (!itemResult.isValid()) { 310 appendPrefixTo(level+1, sb); 311 sb.append("Invalid Item ").append(i+1).append(':') 312 .append(StringUtils.LINE_SEPARATOR); 313 itemResult.appendTextTo(level+1, seq.get(i), sb); 314 } 315 } 316 } 317 } 318 } 319 320 private void appendAttribute(int level, int tag, StringBuilder sb) { 321 appendPrefixTo(level, sb); 322 sb.append(TagUtils.toString(tag)) 323 .append(' ') 324 .append(ElementDictionary.keywordOf(tag, null)); 325 } 326 327 private void appendPrefixTo(int level, StringBuilder sb) { 328 while (level-- > 0) 329 sb.append('>'); 330 } 331 332}