Coverage Report - ca.uhn.hl7v2.model.AbstractGroup
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractGroup
84%
263/312
80%
152/188
4.395
 
 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 "AbstractGroup.java".  Description: 
 10  
 "A partial implementation of Group" 
 11  
 
 12  
 The Initial Developer of the Original Code is University Health Network. Copyright (C) 
 13  
 2001.  All Rights Reserved. 
 14  
 
 15  
 Contributor(s): ______________________________________. 
 16  
 
 17  
 Alternatively, the contents of this file may be used under the terms of the 
 18  
 GNU General Public License (the  "GPL"), in which case the provisions of the GPL are 
 19  
 applicable instead of those above.  If you wish to allow use of your version of this 
 20  
 file only under the terms of the GPL and not to allow others to use your version 
 21  
 of this file under the MPL, indicate your decision by deleting  the provisions above 
 22  
 and replace  them with the notice and other provisions required by the GPL License.  
 23  
 If you do not delete the provisions above, a recipient may use your version of 
 24  
 this file under either the MPL or the GPL. 
 25  
 
 26  
  */
 27  
 
 28  
 package ca.uhn.hl7v2.model;
 29  
 
 30  
 import java.util.ArrayList;
 31  
 import java.util.Collections;
 32  
 import java.util.HashMap;
 33  
 import java.util.HashSet;
 34  
 import java.util.List;
 35  
 import java.util.Map;
 36  
 import java.util.Set;
 37  
 
 38  
 import ca.uhn.hl7v2.HL7Exception;
 39  
 import ca.uhn.hl7v2.VersionLogger;
 40  
 import ca.uhn.hl7v2.parser.EncodingCharacters;
 41  
 import ca.uhn.hl7v2.parser.ModelClassFactory;
 42  
 import ca.uhn.hl7v2.parser.PipeParser;
 43  
 import ca.uhn.hl7v2.util.ReflectionUtil;
 44  
 
 45  
 /**
 46  
  * A partial implementation of Group. Subclasses correspond to specific groups of segments (and/or
 47  
  * other sub-groups) that are implicitly defined by message structures in the HL7 specification. A
 48  
  * subclass should define it's group structure by putting repeated calls to the add(...) method in
 49  
  * it's constructor. Each call to add(...) adds a specific component to the Group.
 50  
  * 
 51  
  * @author Bryan Tripp (bryan_tripp@sourceforge.net)
 52  
  */
 53  
 public abstract class AbstractGroup extends AbstractStructure implements Group {
 54  
 
 55  
     private static final int PS_INDENT = 3;
 56  
 
 57  
         private static final long serialVersionUID = 1772720246448224363L;
 58  
 
 59  
     private List<String> names;
 60  
     private Map<String, List<Structure>> structures;
 61  
     private Map<String, Boolean> required;
 62  
     private Map<String, Boolean> repeating;
 63  
     private Set<String> choiceElements;
 64  
     private Map<String, Class<? extends Structure>> classes;
 65  
     // protected Message message;
 66  
     private Set<String> nonStandardNames;
 67  
     private final ModelClassFactory myFactory;
 68  
 
 69  
     static {
 70  1
         VersionLogger.init();
 71  1
     }
 72  
 
 73  
     /**
 74  
      * This constructor should be used by implementing classes that do not also implement Message.
 75  
      * 
 76  
      * @param parent the group to which this Group belongs.
 77  
      * @param factory the factory for classes of segments, groups, and datatypes under this group
 78  
      */
 79  
     protected AbstractGroup(Group parent, ModelClassFactory factory) {
 80  10059
         super(parent);
 81  10059
         this.myFactory = factory;
 82  10059
         init();
 83  10059
     }
 84  
 
 85  
     private void init() {
 86  10059
         names = new ArrayList<String>();
 87  10059
         structures = new HashMap<String, List<Structure>>();
 88  10059
         required = new HashMap<String, Boolean>();
 89  10059
         repeating = new HashMap<String, Boolean>();
 90  10059
         classes = new HashMap<String, Class<? extends Structure>>();
 91  10059
         choiceElements = new HashSet<String>();
 92  10059
     }
 93  
 
 94  
     /**
 95  
      * Returns the named structure. If this Structure is repeating then the first repetition is
 96  
      * returned. Creates the Structure if necessary.
 97  
      * 
 98  
      * @throws HL7Exception if the named Structure is not part of this Group.
 99  
      */
 100  
     public Structure get(String name) throws HL7Exception {
 101  29697
         return get(name, 0);
 102  
     }
 103  
 
 104  
     protected <T extends Structure> T getTyped(String name, Class<T> type) {
 105  
         try {
 106  
             @SuppressWarnings("unchecked")
 107  615
             T ret = (T) get(name);
 108  615
             return ret;
 109  0
         } catch (HL7Exception e) {
 110  0
             log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e);
 111  0
             throw new RuntimeException(e);
 112  
         }
 113  
     }
 114  
 
 115  
     /**
 116  
      * Returns a particular repetition of the named Structure. If the given repetition number is one
 117  
      * greater than the existing number of repetitions then a new Structure is created.
 118  
      * 
 119  
      * @throws HL7Exception if the named Structure is not part of this group, if the structure is
 120  
      *             not repeatable and the given rep is > 0, or if the given repetition number is
 121  
      *             more than one greater than the existing number of repetitions.
 122  
      */
 123  
     public Structure get(String name, int rep) throws HL7Exception {
 124  53873
         List<Structure> list = structures.get(name);
 125  53873
         if (list == null)
 126  5
             throw new HL7Exception(name + " does not exist in the group " + this.getClass().getName());
 127  
 
 128  
         Structure ret;
 129  53868
         if (rep < list.size()) {
 130  
             // return existing Structure if it exists
 131  20575
             ret = list.get(rep);
 132  33293
         } else if (rep == list.size()) {
 133  
             // verify that Structure is repeating ...
 134  33291
             Boolean repeats = this.repeating.get(name);
 135  33291
             if (!repeats && list.size() > 0)
 136  0
                 throw new HL7Exception("Can't create repetition #" + rep + " of Structure " + name
 137  
                         + " - this Structure is non-repeating so only rep 0 may be retrieved");
 138  
 
 139  
             // create a new Structure, add it to the list, and return it
 140  33291
             Class<? extends Structure> c = classes.get(name); // get class
 141  33291
             ret = tryToInstantiateStructure(c, name);
 142  33291
             list.add(ret);
 143  33291
         } else {
 144  2
             StringBuilder b = new StringBuilder();
 145  2
                         b.append("Can't return repetition #");
 146  2
                         b.append(rep);
 147  2
                         b.append(" of ");
 148  2
                         b.append(name);
 149  2
                         b.append(" - there are currently ");
 150  2
                         if (list.size() == 0) {
 151  1
                                 b.append("no");
 152  
                         } else {
 153  1
                                 b.append("only ");
 154  1
                                 b.append(list.size());
 155  
                         }
 156  2
                         b.append(" repetitions ");
 157  2
                         b.append("so rep# must be ");
 158  2
                         if (list.size() == 0) {
 159  1
                                 b.append("0");
 160  
                         } else {
 161  1
                                 b.append("between 0 and ");
 162  1
                                 b.append(list.size());
 163  
                         }
 164  2
                         throw new HL7Exception(b.toString());
 165  
         }
 166  53866
         return ret;
 167  
     }
 168  
 
 169  
     /**
 170  
      * {@inheritDoc}
 171  
      */
 172  
     public boolean isEmpty() throws HL7Exception {
 173  10
         for (String name : getNames()) {
 174  10
             if (!get(name).isEmpty())
 175  8
                 return false;
 176  
         }
 177  0
         return true;
 178  
     }
 179  
 
 180  
     protected <T extends Structure> T getTyped(String name, int rep, Class<T> type) {
 181  
         try {
 182  
             @SuppressWarnings("unchecked")
 183  163
             T ret = (T) get(name, rep);
 184  161
             return ret;
 185  2
         } catch (HL7Exception e) {
 186  2
                 List<Structure> list = structures.get(name);
 187  2
                 if (list != null && list.size() < rep) {
 188  
                         // This is programmer/user error so don't report that it's a bug in the generator
 189  
                 } else {
 190  0
                         log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e);
 191  
                 }
 192  2
             throw new RuntimeException(e);
 193  
         }
 194  
     }
 195  
 
 196  
     protected int getReps(String name) {
 197  
         try {
 198  3
             return getAll(name).length;
 199  0
         } catch (HL7Exception e) {
 200  0
             String message = "Unexpected error accessing data - this is probably a bug in the source code generator.";
 201  0
             log.error(message, e);
 202  0
             throw new RuntimeException(message);
 203  
         }
 204  
     }
 205  
 
 206  
     /**
 207  
      * Expands the group definition to include a segment that is not defined by HL7 to be part of
 208  
      * this group (eg an unregistered Z segment). The new segment is slotted at the end of the
 209  
      * group. Thenceforward if such a segment is encountered it will be parsed into this location.
 210  
      * If the segment name is unrecognized a GenericSegment is used. The segment is defined as
 211  
      * repeating and not required.
 212  
      */
 213  
     public String addNonstandardSegment(String name) throws HL7Exception {
 214  172
         String version = this.getMessage().getVersion();
 215  172
         if (version == null)
 216  0
             throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null");
 217  172
         Class<? extends Segment> c = myFactory.getSegmentClass(name, version);
 218  172
         if (c == null)
 219  9
             c = GenericSegment.class;
 220  
 
 221  172
         int index = this.getNames().length;
 222  
 
 223  172
         tryToInstantiateStructure(c, name); // may throw exception
 224  
 
 225  172
         String newName = insert(c, false, true, index, name);
 226  172
         if (this.nonStandardNames == null) {
 227  150
             this.nonStandardNames = new HashSet<String>();
 228  
         }
 229  172
         this.nonStandardNames.add(newName);
 230  
 
 231  172
         return newName;
 232  
     }
 233  
 
 234  
     public String addNonstandardSegment(String theName, int theIndex) throws HL7Exception {
 235  182
         if (this instanceof Message && theIndex == 0) {
 236  0
             throw new HL7Exception("Can not add nonstandard segment \"" + theName + "\" to start of message.");
 237  
         }
 238  
 
 239  182
         String version = this.getMessage().getVersion();
 240  182
         if (version == null)
 241  0
             throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null");
 242  182
         Class<? extends Segment> c = myFactory.getSegmentClass(theName, version);
 243  
 
 244  182
         if (c == null) {
 245  44
             c = GenericSegment.class;
 246  
         }
 247  
 
 248  182
         tryToInstantiateStructure(c, theName); // may throw exception
 249  
 
 250  182
         String newName = insert(c, false, true, theIndex, theName);
 251  182
         if (this.nonStandardNames == null) {
 252  80
             this.nonStandardNames = new HashSet<String>();
 253  
         }
 254  182
         this.nonStandardNames.add(newName);
 255  
 
 256  182
         return newName;
 257  
     }
 258  
 
 259  
     /**
 260  
      * Returns a Set containing the names of all non-standard structures which have been added to
 261  
      * this structure
 262  
      */
 263  
     public Set<String> getNonStandardNames() {
 264  0
         if (nonStandardNames == null) {
 265  0
             return Collections.emptySet();
 266  
         }
 267  0
         return Collections.unmodifiableSet(nonStandardNames);
 268  
     }
 269  
 
 270  
     /**
 271  
      * Returns an ordered array of the names of the Structures in this Group. These names can be
 272  
      * used to iterate through the group using repeated calls to <code>get(name)</code>.
 273  
      */
 274  
     public String[] getNames() {
 275  36516
         String[] retVal = new String[this.names.size()];
 276  218140
         for (int i = 0; i < this.names.size(); i++) {
 277  181624
             retVal[i] = this.names.get(i);
 278  
         }
 279  36516
         return retVal;
 280  
     }
 281  
 
 282  
     /**
 283  
      * Adds a new Structure (group or segment) to this Group. A place for the Structure is added to
 284  
      * the group but there are initially zero repetitions. This method should be used by the
 285  
      * constructors of implementing classes to specify which Structures the Group contains -
 286  
      * Structures should be added in the order in which they appear. Note that the class is supplied
 287  
      * instead of an instance because we want there initially to be zero instances of each structure
 288  
      * but we want the AbstractGroup code to be able to create instances as necessary to support
 289  
      * get(...) calls.
 290  
      * 
 291  
      * @return the actual name used to store this structure (may be appended with an integer if
 292  
      *         there are duplicates in the same Group).
 293  
      */
 294  
     protected String add(Class<? extends Structure> c, boolean required, boolean repeating) throws HL7Exception {
 295  34753
             return add(c, required, repeating, false);
 296  
     }
 297  
 
 298  
     /**
 299  
      * Adds a new Structure (group or segment) to this Group. A place for the Structure is added to
 300  
      * the group but there are initially zero repetitions. This method should be used by the
 301  
      * constructors of implementing classes to specify which Structures the Group contains -
 302  
      * Structures should be added in the order in which they appear. Note that the class is supplied
 303  
      * instead of an instance because we want there initially to be zero instances of each structure
 304  
      * but we want the AbstractGroup code to be able to create instances as necessary to support
 305  
      * get(...) calls.
 306  
      * 
 307  
      * @return the actual name used to store this structure (may be appended with an integer if
 308  
      *         there are duplicates in the same Group).
 309  
      */
 310  
     protected String add(Class<? extends Structure> c, boolean required, boolean repeating, boolean choiceElement) throws HL7Exception {
 311  55874
         String name = getName(c);
 312  55874
         return insert(c, required, repeating, choiceElement, this.names.size(), name);
 313  
         }
 314  
 
 315  
         /**
 316  
      * Adds a new Structure (group or segment) to this Group. A place for the Structure is added to
 317  
      * the group but there are initially zero repetitions. This method should be used by the
 318  
      * constructors of implementing classes to specify which Structures the Group contains -
 319  
      * Structures should be added in the order in which they appear. Note that the class is supplied
 320  
      * instead of an instance because we want there initially to be zero instances of each structure
 321  
      * but we want the AbstractGroup code to be able to create instances as necessary to support
 322  
      * get(...) calls.
 323  
      * 
 324  
      * @return the actual name used to store this structure (may be appended with an integer if
 325  
      *         there are duplicates in the same Group).
 326  
      */
 327  
     protected String add(Class<? extends Structure> c, boolean required, boolean repeating, int index)
 328  
             throws HL7Exception {
 329  0
         String name = getName(c);
 330  0
         return insert(c, required, repeating, index, name);
 331  
     }
 332  
 
 333  
     /**
 334  
      * Returns true if the class name is already being used.
 335  
      */
 336  
     private boolean nameExists(String name) {
 337  58690
         return this.classes.get(name) != null;
 338  
     }
 339  
 
 340  
     /**
 341  
      * Attempts to create an instance of the given class and return it as a Structure.
 342  
      * 
 343  
      * @param c the Structure implementing class
 344  
      * @param name an optional name of the structure (used by Generic structures; may be null)
 345  
      */
 346  
     protected Structure tryToInstantiateStructure(Class<? extends Structure> c, String name) throws HL7Exception {
 347  33676
         if (GenericSegment.class.isAssignableFrom(c)) {
 348  111
             String genericName = name;
 349  111
             if (genericName.length() > 3) {
 350  12
                 genericName = genericName.substring(0, 3);
 351  
             }
 352  111
             return new GenericSegment(this, genericName);
 353  
         }
 354  33565
         if (GenericGroup.class.isAssignableFrom(c)) {
 355  0
             return new GenericGroup(this, name, myFactory);
 356  
         }
 357  
         try {
 358  33565
             return ReflectionUtil.instantiateStructure(c, this, myFactory);
 359  0
         } catch (Exception e) {
 360  0
             return ReflectionUtil.instantiate(c);
 361  
         }
 362  
 
 363  
     }
 364  
 
 365  
         /**
 366  
          * {@inheritDoc}
 367  
          */
 368  
         public boolean isChoiceElement(String theName) throws HL7Exception {
 369  14381
                 return choiceElements.contains(theName);
 370  
         }
 371  
 
 372  
     /**
 373  
      * Returns true if the named structure is a group
 374  
      */
 375  
     public boolean isGroup(String name) throws HL7Exception {
 376  1820
         Class<? extends Structure> clazz = classes.get(name);
 377  1820
         if (clazz == null)
 378  0
             throw new HL7Exception("The structure " + name + " does not exist in the group "
 379  
                     + this.getClass().getName());
 380  1820
         return Group.class.isAssignableFrom(clazz);
 381  
     }
 382  
 
 383  
     /**
 384  
      * Returns true if the named structure is required.
 385  
      */
 386  
     public boolean isRequired(String name) throws HL7Exception {
 387  10729
         Boolean req = required.get(name);
 388  10729
         if (req == null)
 389  0
             throw new HL7Exception("The structure " + name + " does not exist in the group "
 390  
                     + this.getClass().getName());
 391  10729
         return req;
 392  
     }
 393  
 
 394  
     /**
 395  
      * Returns true if the named structure is required.
 396  
      */
 397  
     public boolean isRepeating(String name) throws HL7Exception {
 398  5581
         Boolean rep = repeating.get(name);
 399  5581
         if (rep == null)
 400  0
             throw new HL7Exception("The structure " + name + " does not exist in the group "
 401  
                     + this.getClass().getName());
 402  5581
         return rep;
 403  
     }
 404  
 
 405  
     /**
 406  
      * Returns the number of existing repetitions of the named structure.
 407  
      */
 408  
     public int currentReps(String name) throws HL7Exception {
 409  0
         List<Structure> list = structures.get(name);
 410  0
         if (list == null)
 411  0
             throw new HL7Exception("The structure " + name + " does not exist in the group "
 412  
                     + this.getClass().getName());
 413  0
         return list.size();
 414  
     }
 415  
 
 416  
     /**
 417  
      * Returns an array of Structure objects by name. For example, if the Group contains an MSH
 418  
      * segment and "MSH" is supplied then this call would return a 1-element array containing the
 419  
      * MSH segment. Multiple elements are returned when the segment or group repeats. The array may
 420  
      * be empty if no repetitions have been accessed yet using the get(...) methods.
 421  
      * 
 422  
      * @throws HL7Exception if the named Structure is not part of this Group.
 423  
      */
 424  
     public Structure[] getAll(String name) throws HL7Exception {
 425  23293
         List<Structure> list = structures.get(name);
 426  23293
         if (list == null) {
 427  2
             throw new HL7Exception("The structure " + name + " does not exist in the group "
 428  
                     + this.getClass().getName());
 429  
         }
 430  23291
         return list.toArray(new Structure[list.size()]);
 431  
     }
 432  
 
 433  
     /**
 434  
      * Returns a list containing all existing repetitions of the structure identified by name
 435  
      * 
 436  
      * @throws HL7Exception if the named Structure is not part of this Group.
 437  
      */
 438  
     @SuppressWarnings("unchecked")
 439  
     protected <T extends Structure> List<T> getAllAsList(String name, Class<T> theType) throws HL7Exception {
 440  11
         Class<? extends Structure> clazz = classes.get(name);
 441  
 
 442  11
         if (!theType.equals(clazz)) {
 443  0
             throw new HL7Exception("Structure with name \"" + name + "\" has type " + clazz.getName()
 444  
                     + " but should be " + theType);
 445  
         }
 446  11
         List<T> retVal = new ArrayList<T>();
 447  11
         for (Structure next : structures.get(name)) {
 448  9
             retVal.add((T) next);
 449  9
         }
 450  11
         return Collections.unmodifiableList(retVal);
 451  
     }
 452  
 
 453  
     /**
 454  
      * Removes a repetition of a given Structure objects by name. For example, if the Group contains
 455  
      * 10 repititions an OBX segment and "OBX" is supplied with an index of 2, then this call would
 456  
      * remove the 3rd repetition. Note that in this case, the Set ID field in the OBX segments would
 457  
      * also need to be renumbered manually.
 458  
      * 
 459  
      * @return The removed structure
 460  
      * @throws HL7Exception if the named Structure is not part of this Group.
 461  
      */
 462  
     public Structure removeRepetition(String name, int index) throws HL7Exception {
 463  1
         List<Structure> list = structures.get(name);
 464  1
         if (list == null) {
 465  0
             throw new HL7Exception("The structure " + name + " does not exist in the group "
 466  
                     + this.getClass().getName());
 467  
         }
 468  1
         if (list.size() == 0) {
 469  0
             throw new HL7Exception("Invalid index: " + index + ", structure " + name + " has no repetitions");
 470  
         }
 471  1
         if (list.size() <= index) {
 472  0
             throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and "
 473  
                     + (list.size() - 1));
 474  
         }
 475  
 
 476  1
         return list.remove(index);
 477  
     }
 478  
 
 479  
     /**
 480  
      * Inserts a repetition of a given Structure into repetitions of that structure by name. For
 481  
      * example, if the Group contains 10 repetitions an OBX segment and an OBX is supplied with an
 482  
      * index of 2, then this call would insert the new repetition at index 2. (Note that in this
 483  
      * example, the Set ID field in the OBX segments would also need to be renumbered manually).
 484  
      * 
 485  
      * @throws HL7Exception if the named Structure is not part of this Group.
 486  
      */
 487  
     protected void insertRepetition(String name, Structure structure, int index) throws HL7Exception {
 488  2
         if (structure == null) {
 489  0
             throw new NullPointerException("Structure may not be null");
 490  
         }
 491  
 
 492  2
         if (structure.getMessage() != this.getMessage()) {
 493  0
             throw new HL7Exception("Structure does not belong to this message");
 494  
         }
 495  
 
 496  2
         List<Structure> list = structures.get(name);
 497  
 
 498  2
         if (list == null) {
 499  0
             throw new HL7Exception("The structure " + name + " does not exist in the group "
 500  
                     + this.getClass().getName());
 501  
         }
 502  2
         if (list.size() < index) {
 503  0
             throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and "
 504  
                     + (list.size()));
 505  
         }
 506  
 
 507  2
         list.add(index, structure);
 508  2
     }
 509  
 
 510  
     /**
 511  
      * Inserts a repetition of a given Structure into repetitions of that structure by name. For
 512  
      * example, if the Group contains 10 repititions an OBX segment and an OBX is supplied with an
 513  
      * index of 2, then this call would insert the new repetition at index 2. Note that in this
 514  
      * case, the Set ID field in the OBX segments would also need to be renumbered manually.
 515  
      * 
 516  
      * @return The removed structure
 517  
      * @throws HL7Exception if the named Structure is not part of this Group.
 518  
      */
 519  
     public Structure insertRepetition(String name, int index) throws HL7Exception {
 520  1
         if (name == null || name.length() == 0) {
 521  0
             throw new NullPointerException("Name may not be null/empty");
 522  
         }
 523  
 
 524  1
         Class<? extends Structure> structureClass = this.classes.get(name);
 525  1
         if (structureClass == null) {
 526  0
             throw new HL7Exception("Group " + this.getClass().getName() + " has no structure named " + name
 527  
                     + ": Valid names: " + this.classes.keySet());
 528  
         }
 529  
 
 530  1
         Structure rep = tryToInstantiateStructure(structureClass, name);
 531  1
         insertRepetition(name, rep, index);
 532  
 
 533  1
         return rep;
 534  
     }
 535  
 
 536  
     /**
 537  
      * Given a child structure name, returns the child index (which is 1-indexed, meaning that the
 538  
      * first child is at index 1
 539  
      */
 540  
     public int getFieldNumForName(String name) throws HL7Exception {
 541  0
         int retVal = names.indexOf(name);
 542  0
         if (retVal == -1) {
 543  0
             throw new HL7Exception("Unknown name: " + name);
 544  
         }
 545  0
         return retVal + 1;
 546  
     }
 547  
 
 548  
     /**
 549  
      * Returns the Class of the Structure at the given name index.
 550  
      */
 551  
     public Class<? extends Structure> getClass(String name) {
 552  0
         return classes.get(name);
 553  
     }
 554  
 
 555  
     /**
 556  
      * Returns the class name (excluding package).
 557  
      * 
 558  
      * @see Structure#getName()
 559  
      */
 560  
     public String getName() {
 561  9395
         return getName(getClass());
 562  
     }
 563  
 
 564  
     // returns a name for a class of a Structure in this Message
 565  
     private String getName(Class<? extends Structure> c) {        
 566  82418
         String name = c.getSimpleName();
 567  82418
         if (Group.class.isAssignableFrom(c) && !Message.class.isAssignableFrom(c)) {
 568  17138
             name = getGroupName(name);
 569  
         }
 570  82418
         return name;
 571  
     }
 572  
 
 573  
     /**
 574  
      * Remove message name prefix from group names for compatibility with getters. Due to
 575  
      * 3558962 we also need to look at the message's super classes to enable custom
 576  
      * messages that reuse groups from their ancestors.
 577  
      *
 578  
      * @param name the simple name of the group
 579  
      * @return the abbreviated group in name in case of matching prefixes
 580  
      */
 581  
     private String getGroupName(String name) {
 582  17138
         Class<?> messageClass = getMessage().getClass();
 583  17154
         while (Message.class.isAssignableFrom(messageClass)) {
 584  
             @SuppressWarnings("unchecked")
 585  
             // actually we should call getName() instead of getName(Class), but this
 586  
             // is due to issue 3558962
 587  17149
             String messageName = getName((Class<? extends Message>)messageClass);
 588  17149
             if (name.startsWith(messageName) && name.length() > messageName.length()) {
 589  17133
                 return name.substring(messageName.length() + 1);
 590  
             }
 591  16
             messageClass = messageClass.getSuperclass();
 592  16
         }
 593  5
         return name;
 594  
     }
 595  
     
 596  
 
 597  
 
 598  
     /**
 599  
      * Inserts the given structure into this group, at the indicated index number. This method is
 600  
      * used to support handling of unexpected segments (e.g. Z-segments). In contrast, specification
 601  
      * of the group's normal children should be done at construction time, using the add(...)
 602  
      * method.
 603  
      */
 604  
     protected String insert(Class<? extends Structure> c, boolean required, boolean repeating, int index, String name)
 605  
             throws HL7Exception {
 606  354
             return insert(c, required, repeating, false, index, name);
 607  
     }
 608  
 
 609  
     protected String insert(Class<? extends Structure> c, boolean required, boolean repeating, boolean choiceElement, 
 610  
                     int index, String name) throws HL7Exception {
 611  
         // tryToInstantiateStructure(c, name); //may throw exception
 612  
 
 613  
         // see if there is already something by this name and make a new name if
 614  
         // necessary ...
 615  56228
         if (nameExists(name)) {
 616  1228
             int version = 2;
 617  1228
             String newName = name;
 618  2462
             while (nameExists(newName)) {
 619  1234
                 newName = name + version++;
 620  
             }
 621  1228
             name = newName;
 622  
         }
 623  
 
 624  56228
         if (index > this.names.size()) {
 625  0
             throw new HL7Exception("Invalid index " + index + " - Should be <= " + this.names.size());
 626  
         }
 627  
 
 628  56228
         this.names.add(index, name);
 629  56228
         this.required.put(name, new Boolean(required));
 630  56228
         this.repeating.put(name, new Boolean(repeating));
 631  56228
         this.classes.put(name, c);
 632  56228
         this.structures.put(name, new ArrayList<Structure>());
 633  
         
 634  56228
         if (choiceElement) {
 635  395
                 this.choiceElements.add(name);
 636  
         }
 637  
 
 638  56228
         return name;
 639  
         }
 640  
 
 641  
         /**
 642  
      * Clears all data from this structure.
 643  
      */
 644  
     public void clear() {
 645  73
         for (List<Structure> next : structures.values()) {
 646  1057
             if (next != null) {
 647  1057
                 next.clear();
 648  
             }
 649  1057
         }
 650  73
     }
 651  
 
 652  
     /**
 653  
      * Returns the {@link ModelClassFactory} associated with this structure
 654  
      */
 655  
     public final ModelClassFactory getModelClassFactory() {
 656  0
         return myFactory;
 657  
     }
 658  
 
 659  
     /**
 660  
      * <p>
 661  
      * Appends a description of this group's structure and all children's structure to a string
 662  
      * builder.
 663  
      * </p>
 664  
      * <p>
 665  
      * Note that this method is intended only to be called by
 666  
      * {@link AbstractMessage#printStructure()}. Please use caution if calling this method directly,
 667  
      * as the method signature and/or behaviour may change in the future.
 668  
      * </p>
 669  
      */
 670  
     void appendStructureDescription(StringBuilder theStringBuilder, int theIndent, boolean theOptional,
 671  
             boolean theRepeating, boolean theAddStartName, boolean theAddEndName, boolean thePrintEmpty) throws HL7Exception {
 672  75
         String lineSeparator = System.getProperty("line.separator");
 673  
 
 674  75
         if (theAddStartName) {
 675  68
             indent(theStringBuilder, theIndent);
 676  68
             theStringBuilder.append(getName()).append(" (start)").append(lineSeparator);
 677  
         }
 678  
 
 679  75
         if (theOptional || theRepeating) {
 680  64
             indent(theStringBuilder, theIndent);
 681  64
             if (theOptional) {
 682  44
                 theStringBuilder.append("[");
 683  
             }
 684  64
             if (theRepeating) {
 685  41
                 theStringBuilder.append("{");
 686  
             }
 687  64
             theStringBuilder.append(lineSeparator);
 688  
         }
 689  
 
 690  75
         boolean inChoice = false;
 691  
         
 692  440
         for (String nextName : getNames()) {
 693  
                 
 694  365
                 if (!thePrintEmpty) {
 695  75
                         boolean hasContent = false;
 696  75
                         Structure[] allReps = getAll(nextName);
 697  75
                         for (Structure structure : allReps) {
 698  17
                                         if (!structure.isEmpty()) {
 699  17
                                                 hasContent = true;
 700  17
                                                 break;
 701  
                                         }
 702  
                                 }
 703  
                         
 704  75
                         if (!hasContent) {
 705  58
                                 continue;
 706  
                         }
 707  
                 }
 708  
 
 709  307
             Class<? extends Structure> nextClass = classes.get(nextName);
 710  
 
 711  307
             boolean nextOptional = !isRequired(nextName);
 712  307
             boolean nextRepeating = isRepeating(nextName);
 713  307
             boolean nextChoice = isChoiceElement(nextName);
 714  
 
 715  307
             if (nextChoice && !inChoice) {
 716  1
                     theIndent += PS_INDENT;
 717  1
                 indent(theStringBuilder, theIndent);
 718  1
                 theStringBuilder.append("<");
 719  1
                 theStringBuilder.append(lineSeparator);
 720  1
                 inChoice = true;
 721  306
             } else if (!nextChoice && inChoice) {
 722  1
                 indent(theStringBuilder, theIndent);
 723  1
                 theStringBuilder.append(">");
 724  1
                 theStringBuilder.append(lineSeparator);
 725  1
                 inChoice = false;
 726  1
                 theIndent -= PS_INDENT;
 727  305
             } else if (nextChoice && inChoice) {
 728  5
                 indent(theStringBuilder, theIndent);
 729  5
                 theStringBuilder.append("|");
 730  5
                 theStringBuilder.append(lineSeparator);
 731  
             }
 732  
             
 733  307
             if (AbstractGroup.class.isAssignableFrom(nextClass)) {
 734  
 
 735  57
                 Structure[] nextChildren = getAll(nextName);
 736  91
                 for (int i = 0; i < nextChildren.length; i++) {
 737  
 
 738  34
                     Structure structure = nextChildren[i];
 739  34
                     boolean addStartName = (i == 0);
 740  34
                     boolean addEndName = (i == (nextChildren.length - 1));
 741  34
                     ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT,
 742  
                             nextOptional, nextRepeating, addStartName, addEndName, thePrintEmpty);
 743  
 
 744  
                 }
 745  
 
 746  57
                 if (nextChildren.length == 0) {
 747  30
                     Structure structure = tryToInstantiateStructure(nextClass, nextName);
 748  30
                     ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT,
 749  
                             nextOptional, nextRepeating, true, true, thePrintEmpty);
 750  
                 }
 751  
 
 752  57
             } else if (Segment.class.isAssignableFrom(nextClass)) {
 753  
 
 754  250
                 int currentIndent = theStringBuilder.length();
 755  
 
 756  250
                 StringBuilder structurePrefix = new StringBuilder();
 757  250
                 indent(structurePrefix, theIndent + PS_INDENT);
 758  250
                 if (nextOptional) {
 759  161
                     structurePrefix.append("[ ");
 760  
                 }
 761  250
                 if (nextRepeating) {
 762  93
                     structurePrefix.append("{ ");
 763  
                 }
 764  250
                 structurePrefix.append(nextName);
 765  250
                 if (nextRepeating) {
 766  93
                     structurePrefix.append(" }");
 767  
                 }
 768  250
                 if (nextOptional) {
 769  161
                     structurePrefix.append(" ]");
 770  
                 }
 771  
 
 772  250
                 if (this.nonStandardNames != null && this.nonStandardNames.contains(nextName)) {
 773  7
                     structurePrefix.append(" (non-standard)");
 774  
                 }
 775  250
                 structurePrefix.append(" - ");
 776  
 
 777  250
                 currentIndent = theStringBuilder.length() - currentIndent;
 778  250
                 List<Structure> nextStructureList = structures.get(nextName);
 779  250
                 theStringBuilder.append(structurePrefix);
 780  250
                 if (nextStructureList == null || nextStructureList.isEmpty()) {
 781  140
                     theStringBuilder.append("Not populated");
 782  140
                     theStringBuilder.append(lineSeparator);
 783  
                 } else {
 784  230
                     for (int i = 0; i < nextStructureList.size(); i++) {
 785  120
                         if (i > 0) {
 786  10
                             indent(theStringBuilder, currentIndent + structurePrefix.length());
 787  
                         }
 788  120
                         Segment nextSegment = (Segment) nextStructureList.get(i);
 789  120
                         theStringBuilder.append(new PipeParser().doEncode(nextSegment,
 790  
                                 EncodingCharacters.getInstance(getMessage())));
 791  120
                         theStringBuilder.append(lineSeparator);
 792  
 
 793  
                     }
 794  
                 }
 795  
 
 796  
             }
 797  
         }
 798  
 
 799  75
         if (inChoice) {
 800  0
             indent(theStringBuilder, theIndent);
 801  0
             theStringBuilder.append(">");
 802  0
             theStringBuilder.append(lineSeparator);
 803  0
             theIndent -= PS_INDENT;
 804  
         }
 805  
         
 806  75
         if (theOptional || theRepeating) {
 807  64
             indent(theStringBuilder, theIndent);
 808  64
             if (theRepeating) {
 809  41
                 theStringBuilder.append("}");
 810  
             }
 811  64
             if (theOptional) {
 812  44
                 theStringBuilder.append("]");
 813  
             }
 814  64
             theStringBuilder.append(lineSeparator);
 815  
         }
 816  
 
 817  75
         if (theAddEndName) {
 818  68
             indent(theStringBuilder, theIndent);
 819  68
             theStringBuilder.append(getName()).append(" (end)").append(lineSeparator);
 820  
         }
 821  75
     }
 822  
 
 823  
     private void indent(StringBuilder theStringBuilder, int theIndent) {
 824  3446
         for (int i = 0; i < theIndent; i++) {
 825  2915
             theStringBuilder.append(' ');
 826  
         }
 827  531
     }
 828  
 }