Coverage Report - ca.uhn.hl7v2.model.Varies
 
Classes in this File Line Coverage Branch Coverage Complexity
Varies
76%
87/113
66%
45/68
3.353
 
 1  
 /**
 2  
 The contents of this file are subject to the Mozilla Public License Version 1.1 
 3  
 (the "License"); you may not use this file except in compliance with the License. 
 4  
 You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
 5  
 Software distributed under the License is distributed on an "AS IS" basis, 
 6  
 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
 7  
 specific language governing rights and limitations under the License. 
 8  
 
 9  
 The Original Code is "Varies.java".  Description: 
 10  
 "Varies is a Type used as a placeholder for another Type in cases where 
 11  
   the appropriate Type is not known until run-time (e.g" 
 12  
 
 13  
 The Initial Developer of the Original Code is University Health Network. Copyright (C) 
 14  
 2001.  All Rights Reserved. 
 15  
 
 16  
 Contributor(s): ______________________________________. 
 17  
 
 18  
 Alternatively, the contents of this file may be used under the terms of the 
 19  
 GNU General Public License (the "GPL"), in which case the provisions of the GPL are 
 20  
 applicable instead of those above.  If you wish to allow use of your version of this 
 21  
 file only under the terms of the GPL and not to allow others to use your version 
 22  
 of this file under the MPL, indicate your decision by deleting  the provisions above 
 23  
 and replace  them with the notice and other provisions required by the GPL License.  
 24  
 If you do not delete the provisions above, a recipient may use your version of 
 25  
 this file under either the MPL or the GPL. 
 26  
 
 27  
 */
 28  
 
 29  
 package ca.uhn.hl7v2.model;
 30  
 
 31  
 import org.slf4j.Logger;
 32  
 import org.slf4j.LoggerFactory;
 33  
 
 34  
 import ca.uhn.hl7v2.ErrorCode;
 35  
 import ca.uhn.hl7v2.HL7Exception;
 36  
 import ca.uhn.hl7v2.parser.EncodingCharacters;
 37  
 import ca.uhn.hl7v2.parser.ModelClassFactory;
 38  
 import ca.uhn.hl7v2.parser.ParserConfiguration;
 39  
 
 40  
 /**
 41  
  * <p>Varies is a Type used as a placeholder for another Type in cases where 
 42  
  * the appropriate Type is not known until run-time (e.g. OBX-5).  
 43  
  * Parsers and validators may have logic that enforces restrictions on the 
 44  
  * Type based on other features of a segment.</p>  
 45  
  * <p>If you want to set both the type and the values of a Varies object, you should
 46  
  * set the type first by calling setData(Type t), keeping a reference to your Type, 
 47  
  * and then set values by calling methods on the Type.  Here is an example:</p>
 48  
  * <p><code>CN cn = new CN();<br>
 49  
  * variesObject.setData(cn);<br>
 50  
  * cn.getIDNumber().setValue("foo");</code></p>
 51  
  * 
 52  
  * @author Bryan Tripp (bryan_tripp@users.sourceforge.net)
 53  
  * @author Andy Pardue 
 54  
  * 
 55  
  */
 56  
 @SuppressWarnings("serial")
 57  
 public class Varies implements Type {
 58  
 
 59  
         /** 
 60  
          * System property key: The value may be set to provide a default
 61  
          * datatype ("ST", "NM", etc) for an OBX segment with a missing
 62  
          * OBX-2 value.
 63  
          */        
 64  
         public static final String DEFAULT_OBX2_TYPE_PROP = "ca.uhn.hl7v2.model.varies.default_obx2_type";
 65  
 
 66  
     /** 
 67  
      * System property key: The value may be set to provide a default
 68  
      * datatype ("ST", "NM", etc) for an OBX segment with an invalid
 69  
      * OBX-2 value type. In other words, if OBX-2 has a value of "ZYZYZ",
 70  
      * which is not a valid value, but this property is set to "ST", then
 71  
      * OBX-5 will be parsed as an ST.
 72  
      */ 
 73  
     public static final String INVALID_OBX2_TYPE_PROP = "ca.uhn.hl7v2.model.varies.invalid_obx2_type";
 74  
 
 75  
     /** 
 76  
      * <p>
 77  
      * System property key: If this is not set, or set to "true", and a subcomponent delimiter is found within the
 78  
      * value of a Varies of a primitive type, this subcomponent delimiter will be treated as a literal
 79  
      * character instead of a subcomponent delimiter, and will therefore be escaped if the message is
 80  
      * re-encoded. This is handy when dealing with non-conformant sending systems which do not correctly
 81  
      * escape ampersands in OBX-5 values.
 82  
      * </p>
 83  
      * <p>
 84  
      * For example, consider the following OBX-5 segment:
 85  
      * <pre>
 86  
      *    OBX||ST|||Apples, Pears &amp; Bananas|||
 87  
      * </pre>
 88  
      * In this example, the data type is a primitive ST and does not support subcomponents, and the
 89  
      * ampersand is obviously not intended to represent a subcomponent delimiter. If this 
 90  
      * property is set to <code>true</code>, the entire string will be treated as the
 91  
      * value of OBX-5, and if the message is re-encoded the string will appear
 92  
      * as "Apples, Pears \T\ Bananas".
 93  
      * </p>
 94  
      * <p>
 95  
      * If this property is set to anything other than "true", the subcomponent delimiter is treated as a component delimiter, 
 96  
      * so the value after the ampersand is placed into an {@link ExtraComponents extra component}.
 97  
      * </p>
 98  
      */ 
 99  
     public static final String ESCAPE_SUBCOMPONENT_DELIM_IN_PRIMITIVE = "ca.uhn.hl7v2.model.varies.escape_subcomponent_delim_in_primitive";
 100  
     
 101  1
         private static final Logger log = LoggerFactory.getLogger(Varies.class);
 102  
 
 103  
     private Type data;
 104  
     private Message message;
 105  
 
 106  
     /** 
 107  
      * Creates new Varies. 
 108  
      *  
 109  
      * @param message message to which this type belongs
 110  
      */
 111  1259
     public Varies(Message message) {
 112  1259
         data = new GenericPrimitive(message);
 113  1259
         this.message = message;
 114  1259
     }
 115  
 
 116  
     /**
 117  
      * Returns the data contained by this instance of Varies.  Returns a GenericPrimitive unless 
 118  
      * setData() has been called. 
 119  
      */
 120  
     public Type getData() {
 121  2805
         return this.data;
 122  
     }
 123  
 
 124  
     /** @see Type#getName */
 125  
     public String getName() {
 126  0
         String name = "*";
 127  0
         if (this.data != null) {
 128  0
             name = this.data.getName();
 129  
         }
 130  0
         return name;
 131  
     }
 132  
 
 133  
     /**
 134  
      * Sets the data contained by this instance of Varies.  If a data object already exists, 
 135  
      * then its values are copied to the incoming data object before the old one is replaced.  
 136  
      * For example, if getData() returns an ST with the value "19901012" and you call 
 137  
      * setData(new DT()), then subsequent calls to getData() will return the same DT, with the value 
 138  
      * set to "19901012".   
 139  
      */
 140  
     public void setData(Type data) throws DataTypeException {
 141  385
         if (this.data != null) {
 142  385
             if (!(this.data instanceof Primitive) || ((Primitive) this.data).getValue() != null) {
 143  356
                 ca.uhn.hl7v2.util.DeepCopy.copy(this.data, data);
 144  
             }
 145  
         }
 146  385
         this.data = data;
 147  385
     }
 148  
     
 149  
     /** Returns extra components from the underlying Type */
 150  
     public ExtraComponents getExtraComponents() {
 151  2
         return this.data.getExtraComponents();
 152  
     }
 153  
     
 154  
     /**
 155  
      * @return the message to which this Type belongs
 156  
      */
 157  
     public Message getMessage() {
 158  442
         return message;
 159  
     }    
 160  
 
 161  
     /** 
 162  
      * <p>
 163  
      * Sets the data type of field 5 in the given OBX segment to the value of OBX-2.  The argument 
 164  
      * is a Segment as opposed to a particular OBX because it is meant to work with any version.
 165  
      * </p>
 166  
      * <p>
 167  
      * Note that if no value is present in OBX-2, or an invalid value is present in
 168  
      * OBX-2, this method will throw an error. This behaviour can be corrected by using the 
 169  
      * following system properties: {@link #DEFAULT_OBX2_TYPE_PROP} and {@link #INVALID_OBX2_TYPE_PROP},
 170  
      * or by using configuration in {@link ParserConfiguration} 
 171  
      * </p>  
 172  
      */
 173  
     public static void fixOBX5(Segment segment, ModelClassFactory factory) throws HL7Exception {
 174  0
         fixOBX5(segment, factory, segment.getMessage().getParser().getParserConfiguration());
 175  0
     }
 176  
 
 177  
     /** 
 178  
      * <p>
 179  
      * Sets the data type of field 5 in the given OBX segment to the value of OBX-2.  The argument 
 180  
      * is a Segment as opposed to a particular OBX because it is meant to work with any version.
 181  
      * </p>
 182  
      * <p>
 183  
      * Note that if no value is present in OBX-2, or an invalid value is present in
 184  
      * OBX-2, this method will throw an error. This behaviour can be corrected by using the 
 185  
      * following system properties: {@link #DEFAULT_OBX2_TYPE_PROP} and {@link #INVALID_OBX2_TYPE_PROP} 
 186  
      * or by using configuration in {@link ParserConfiguration} 
 187  
      * </p>  
 188  
      */
 189  
         public static void fixOBX5(Segment segment, ModelClassFactory factory, ParserConfiguration parserConfiguration) throws HL7Exception {
 190  
                 try {
 191  
             //get unqualified class name
 192  148
             Primitive obx2 = (Primitive) segment.getField(2, 0);
 193  148
             Type[] reps = segment.getField(5);
 194  396
             for (int i = 0; i < reps.length; i++) {
 195  249
                 Varies v = (Varies)reps[i];
 196  
 
 197  
                 // If we don't have a value for OBX-2, a default
 198  
                 // can be supplied via a System property
 199  249
                 if (obx2.getValue() == null) {
 200  0
                         String defaultOBX2Type = parserConfiguration.getDefaultObx2Type();
 201  0
                         if (defaultOBX2Type == null) {
 202  0
                                 defaultOBX2Type = System.getProperty(DEFAULT_OBX2_TYPE_PROP);
 203  
                         }
 204  0
                                         if (defaultOBX2Type != null) {
 205  0
                             log.debug("setting default obx2 type to {}", defaultOBX2Type);
 206  0
                             obx2.setValue(defaultOBX2Type);
 207  
                         }
 208  
                 } // if
 209  
                 
 210  249
                 if (obx2.getValue() == null) {
 211  0
                     if (v.getData() != null) {
 212  0
                         if (!(v.getData() instanceof Primitive) || ((Primitive) v.getData()).getValue() != null) {
 213  0
                             throw new HL7Exception(
 214  
                                 "OBX-5 is valued, but OBX-2 is not.  A datatype for OBX-5 must be specified using OBX-2. See JavaDoc for Varies#fixOBX5(Segment, ModelClassFactory)",
 215  
                                 ErrorCode.REQUIRED_FIELD_MISSING);
 216  
                         }
 217  
                     }
 218  
                 }
 219  
                 else {
 220  
                     //set class
 221  249
                     String version = segment.getMessage().getVersion();
 222  249
                                         String obx2Value = obx2.getValue();
 223  249
                                         Class<? extends Type> c = factory.getTypeClass(obx2Value, version);
 224  
 //                    Class c = ca.uhn.hl7v2.parser.Parser.findClass(obx2.getValue(), 
 225  
 //                                                    segment.getMessage().getVersion(), 
 226  
 //                                                    "datatype");
 227  249
                     if (c == null) {
 228  
                         
 229  2
                         String defaultOBX2Type = parserConfiguration.getInvalidObx2Type();
 230  2
                         if (defaultOBX2Type == null) {
 231  2
                                 defaultOBX2Type = System.getProperty(INVALID_OBX2_TYPE_PROP);
 232  
                         }
 233  2
                         if (defaultOBX2Type != null) {
 234  1
                             c = factory.getTypeClass(defaultOBX2Type, version);
 235  
                         }
 236  
                         
 237  2
                         if (c == null) {
 238  1
                                 Primitive obx1 = (Primitive) segment.getField(1, 0);
 239  1
                                 HL7Exception h = new HL7Exception("\'" +
 240  
                                         obx2.getValue() + "\' in record " +
 241  
                                         obx1.getValue() + " is invalid for version " + version + 
 242  
                                         ". See JavaDoc for Varies#fixOBX5(Segment, ModelClassFactory)",
 243  
                                         ErrorCode.DATA_TYPE_ERROR);
 244  1
                                 h.setSegmentName("OBX");
 245  1
                                 h.setFieldPosition(2);
 246  1
                                 throw h;
 247  
                         }
 248  
                     }
 249  
 
 250  
                     Type newTypeInstance;
 251  
                     try {
 252  248
                         newTypeInstance = (Type) c.getConstructor(new Class[]{Message.class}).newInstance(new Object[]{v.getMessage()});
 253  0
                     } catch (NoSuchMethodException e) {
 254  0
                         newTypeInstance = (Type) c.getConstructor(new Class[]{Message.class, Integer.class}).newInstance(new Object[]{v.getMessage(), 0});
 255  248
                     }
 256  
                     
 257  248
                     boolean escapeSubcomponentDelimInPrimitive = 
 258  
                             parserConfiguration.isEscapeSubcomponentDelimiterInPrimitive() ||
 259  
                             escapeSubcomponentDelimInPrimitive();
 260  
                     
 261  
                     
 262  248
                     if (newTypeInstance instanceof Primitive) {
 263  235
                             Type[] subComponentsInFirstField = v.getFirstComponentSubcomponentsOnlyIfMoreThanOne();
 264  235
                             if (subComponentsInFirstField != null) {
 265  
                                     
 266  6
                                     if (escapeSubcomponentDelimInPrimitive) {
 267  
                                     
 268  5
                                             StringBuilder firstComponentValue = new StringBuilder();
 269  16
                                             for (Type type : subComponentsInFirstField) {
 270  11
                                                     if (firstComponentValue.length() != 0) {
 271  6
                                                             char subComponentSeparator = EncodingCharacters.getInstance(segment.getMessage()).getSubcomponentSeparator();
 272  6
                                                             firstComponentValue.append(subComponentSeparator);
 273  
                                                     }
 274  11
                                                     firstComponentValue.append(type.encode());
 275  
                                                                 }
 276  
                                             
 277  5
                                             v.setFirstComponentPrimitiveValue(firstComponentValue.toString());
 278  
                                     
 279  
                                     } 
 280  
                                     
 281  
                             }
 282  
                     }
 283  
                     
 284  248
                     v.setData(newTypeInstance);
 285  
                 }
 286  
                 
 287  
             } // for reps
 288  
             
 289  
         }
 290  1
         catch (HL7Exception e) {
 291  1
             throw e;
 292  
         }
 293  0
         catch (Exception e) {
 294  0
             throw new HL7Exception(
 295  
                 e.getClass().getName() + " trying to set data type of OBX-5", e);
 296  147
         }
 297  147
         }
 298  
 
 299  
     
 300  
     private static boolean escapeSubcomponentDelimInPrimitive() {
 301  247
                 String property = System.getProperty(ESCAPE_SUBCOMPONENT_DELIM_IN_PRIMITIVE);
 302  247
                 return property == null || "true".equalsIgnoreCase(property);
 303  
         }
 304  
 
 305  
         private void setFirstComponentPrimitiveValue(String theValue) throws DataTypeException {
 306  5
                 Composite c = (Composite) data;
 307  5
                 Type firstComponent = c.getComponent(0);
 308  5
                 setFirstComponentPrimitiveValue(firstComponent, theValue);
 309  5
         }
 310  
 
 311  
     
 312  
         private void setFirstComponentPrimitiveValue(Type theFirstComponent, String theValue)
 313  
                         throws DataTypeException {
 314  
                 
 315  16
                 if (theFirstComponent instanceof Varies) {
 316  16
                         Varies firstComponentVaries = (Varies)theFirstComponent;
 317  16
                         if (((Varies) theFirstComponent).getData() instanceof Composite) {
 318  5
                                 Type[] subComponents = ((Composite)firstComponentVaries.getData()).getComponents();
 319  5
                                 setFirstComponentPrimitiveValue(subComponents[0], theValue);
 320  11
                                 for (int i = 1; i < subComponents.length; i++) {
 321  6
                                         setFirstComponentPrimitiveValue(subComponents[i], "");
 322  
                                 }
 323  5
                         } else {
 324  11
                                 Primitive p = (Primitive) firstComponentVaries.getData();
 325  11
                                 p.setValue(theValue);
 326  
                         }
 327  16
                 } else if (theFirstComponent instanceof Composite) {
 328  0
                         Type[] subComponents = ((Composite)theFirstComponent).getComponents();
 329  0
                         setFirstComponentPrimitiveValue(subComponents[0], theValue);
 330  0
                         for (int i = 1; i < subComponents.length; i++) {
 331  0
                                 setFirstComponentPrimitiveValue(subComponents[i], "");
 332  
                         }
 333  0
                 } else {
 334  0
                         ((Primitive)theFirstComponent).setValue(theValue);
 335  
                 }
 336  16
         }
 337  
 
 338  
         /**
 339  
      * Returns an array containing the subcomponents within the first component of this Varies
 340  
      * object only if there are more than one of them. Otherwise, returns null.
 341  
      */
 342  
     private Type[] getFirstComponentSubcomponentsOnlyIfMoreThanOne() throws DataTypeException {
 343  235
             if (data instanceof Composite) {
 344  7
                     Composite c = (Composite) data;
 345  7
                     Type firstComponent = c.getComponent(0);
 346  7
                     if (firstComponent instanceof Varies) {
 347  7
                             Varies firstComponentVaries = (Varies) firstComponent;
 348  7
                             if (firstComponentVaries.getData() instanceof Composite) {
 349  6
                                     return ((Composite)firstComponentVaries.getData()).getComponents();
 350  
                             }
 351  
                     } 
 352  
             }
 353  229
                 return null;
 354  
         }
 355  
 
 356  
         /**
 357  
      * {@inheritDoc }
 358  
      */
 359  
     public void parse(String string) throws HL7Exception {
 360  13
         if (data != null) {
 361  13
                 data.clear();
 362  
         }
 363  13
             getMessage().getParser().parse(this, string, EncodingCharacters.getInstance(getMessage()));
 364  13
     }
 365  
 
 366  
     /**
 367  
      * {@inheritDoc }
 368  
      */
 369  
     public String encode() throws HL7Exception {
 370  20
         return getMessage().getParser().doEncode(this, EncodingCharacters.getInstance(getMessage()));
 371  
     }
 372  
 
 373  
         /**
 374  
          * {@inheritDoc }
 375  
          */
 376  
         public void clear() {
 377  1
                 data.clear();
 378  1
         }
 379  
         
 380  
         /**
 381  
          * {@inheritDoc }
 382  
          */                
 383  
         public boolean isEmpty() {
 384  1
                 return data.isEmpty();
 385  
         }
 386  
 
 387  
         /**
 388  
          * {@inheritDoc }
 389  
          */
 390  
         public String toString() {
 391  0
                 return AbstractType.toString(this);
 392  
         }
 393  
         
 394  
 }