001/** 002The contents of this file are subject to the Mozilla Public License Version 1.1 003(the "License"); you may not use this file except in compliance with the License. 004You may obtain a copy of the License at http://www.mozilla.org/MPL/ 005Software distributed under the License is distributed on an "AS IS" basis, 006WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 007specific language governing rights and limitations under the License. 008 009The Original Code is "AbstractGroup.java". Description: 010"A partial implementation of Group" 011 012The Initial Developer of the Original Code is University Health Network. Copyright (C) 0132001. All Rights Reserved. 014 015Contributor(s): ______________________________________. 016 017Alternatively, the contents of this file may be used under the terms of the 018GNU General Public License (the "GPL"), in which case the provisions of the GPL are 019applicable instead of those above. If you wish to allow use of your version of this 020file only under the terms of the GPL and not to allow others to use your version 021of this file under the MPL, indicate your decision by deleting the provisions above 022and replace them with the notice and other provisions required by the GPL License. 023If you do not delete the provisions above, a recipient may use your version of 024this file under either the MPL or the GPL. 025 026 */ 027 028package ca.uhn.hl7v2.model; 029 030import java.util.ArrayList; 031import java.util.Collections; 032import java.util.HashMap; 033import java.util.HashSet; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037 038import ca.uhn.hl7v2.HL7Exception; 039import ca.uhn.hl7v2.Location; 040import ca.uhn.hl7v2.VersionLogger; 041import ca.uhn.hl7v2.parser.EncodingCharacters; 042import ca.uhn.hl7v2.parser.ModelClassFactory; 043import ca.uhn.hl7v2.parser.PipeParser; 044import ca.uhn.hl7v2.util.ReflectionUtil; 045 046/** 047 * A partial implementation of Group. Subclasses correspond to specific groups of segments (and/or 048 * other sub-groups) that are implicitly defined by message structures in the HL7 specification. A 049 * subclass should define it's group structure by putting repeated calls to the add(...) method in 050 * it's constructor. Each call to add(...) adds a specific component to the Group. 051 * 052 * @author Bryan Tripp (bryan_tripp@sourceforge.net) 053 */ 054public abstract class AbstractGroup extends AbstractStructure implements Group { 055 056 private static final int PS_INDENT = 3; 057 058 private static final long serialVersionUID = 1772720246448224363L; 059 060 private List<String> names; 061 private Map<String, List<Structure>> structures; 062 private Map<String, Boolean> required; 063 private Map<String, Boolean> repeating; 064 private Set<String> choiceElements; 065 private Map<String, Class<? extends Structure>> classes; 066 067 private Set<String> nonStandardNames; 068 private final ModelClassFactory myFactory; 069 070 static { 071 VersionLogger.init(); 072 } 073 074 /** 075 * This constructor should be used by implementing classes that do not also implement Message. 076 * 077 * @param parent the group to which this Group belongs. 078 * @param factory the factory for classes of segments, groups, and datatypes under this group 079 */ 080 protected AbstractGroup(Group parent, ModelClassFactory factory) { 081 super(parent); 082 this.myFactory = factory; 083 init(); 084 } 085 086 private void init() { 087 names = new ArrayList<String>(); 088 structures = new HashMap<String, List<Structure>>(); 089 required = new HashMap<String, Boolean>(); 090 repeating = new HashMap<String, Boolean>(); 091 classes = new HashMap<String, Class<? extends Structure>>(); 092 choiceElements = new HashSet<String>(); 093 } 094 095 /** 096 * Returns the named structure. If this Structure is repeating then the first repetition is 097 * returned. Creates the Structure if necessary. 098 * 099 * @throws HL7Exception if the named Structure is not part of this Group. 100 */ 101 public Structure get(String name) throws HL7Exception { 102 return get(name, 0); 103 } 104 105 protected <T extends Structure> T getTyped(String name, Class<T> type) { 106 try { 107 @SuppressWarnings("unchecked") 108 T ret = (T) get(name); 109 return ret; 110 } catch (HL7Exception e) { 111 log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e); 112 throw new RuntimeException(e); 113 } 114 } 115 116 /** 117 * Returns a particular repetition of the named Structure. If the given repetition number is one 118 * greater than the existing number of repetitions then a new Structure is created. 119 * 120 * @throws HL7Exception if the named Structure is not part of this group, if the structure is 121 * not repeatable and the given rep is > 0, or if the given repetition number is 122 * more than one greater than the existing number of repetitions. 123 */ 124 public Structure get(String name, int rep) throws HL7Exception { 125 List<Structure> list = structures.get(name); 126 if (list == null) 127 throw new HL7Exception(name + " does not exist in the group " + this.getClass().getName()); 128 129 Structure ret; 130 if (rep < list.size()) { 131 // return existing Structure if it exists 132 ret = list.get(rep); 133 } else if (rep == list.size()) { 134 // verify that Structure is repeating ... 135 Boolean repeats = this.repeating.get(name); 136 if (!repeats && list.size() > 0) 137 throw new HL7Exception("Can't create repetition #" + rep + " of Structure " + name 138 + " - this Structure is non-repeating so only rep 0 may be retrieved"); 139 140 // create a new Structure, add it to the list, and return it 141 Class<? extends Structure> c = classes.get(name); // get class 142 ret = tryToInstantiateStructure(c, name); 143 list.add(ret); 144 } else { 145 StringBuilder b = new StringBuilder(); 146 b.append("Can't return repetition #"); 147 b.append(rep); 148 b.append(" of "); 149 b.append(name); 150 b.append(" - there are currently "); 151 if (list.size() == 0) { 152 b.append("no"); 153 } else { 154 b.append("only "); 155 b.append(list.size()); 156 } 157 b.append(" repetitions "); 158 b.append("so rep# must be "); 159 if (list.size() == 0) { 160 b.append("0"); 161 } else { 162 b.append("between 0 and "); 163 b.append(list.size()); 164 } 165 throw new HL7Exception(b.toString()); 166 } 167 return ret; 168 } 169 170 /** 171 * {@inheritDoc} 172 */ 173 public boolean isEmpty() throws HL7Exception { 174 for (String name : getNames()) { 175 if (!get(name).isEmpty()) 176 return false; 177 } 178 return true; 179 } 180 181 protected <T extends Structure> T getTyped(String name, int rep, Class<T> type) { 182 try { 183 @SuppressWarnings("unchecked") 184 T ret = (T) get(name, rep); 185 return ret; 186 } catch (HL7Exception e) { 187 List<Structure> list = structures.get(name); 188 if (list != null && list.size() < rep) { 189 // This is programmer/user error so don't report that it's a bug in the generator 190 } else { 191 log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e); 192 } 193 throw new RuntimeException(e); 194 } 195 } 196 197 protected int getReps(String name) { 198 try { 199 return getAll(name).length; 200 } catch (HL7Exception e) { 201 String message = "Unexpected error accessing data - this is probably a bug in the source code generator."; 202 log.error(message, e); 203 throw new RuntimeException(message); 204 } 205 } 206 207 /** 208 * Expands the group definition to include a segment that is not defined by HL7 to be part of 209 * this group (eg an unregistered Z segment). The new segment is slotted at the end of the 210 * group. Thenceforward if such a segment is encountered it will be parsed into this location. 211 * If the segment name is unrecognized a GenericSegment is used. The segment is defined as 212 * repeating and not required. 213 */ 214 public String addNonstandardSegment(String name) throws HL7Exception { 215 String version = this.getMessage().getVersion(); 216 if (version == null) 217 throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null"); 218 Class<? extends Segment> c = myFactory.getSegmentClass(name, version); 219 if (c == null) 220 c = GenericSegment.class; 221 222 int index = this.getNames().length; 223 224 tryToInstantiateStructure(c, name); // may throw exception 225 226 String newName = insert(c, false, true, index, name); 227 if (this.nonStandardNames == null) { 228 this.nonStandardNames = new HashSet<String>(); 229 } 230 this.nonStandardNames.add(newName); 231 232 return newName; 233 } 234 235 public String addNonstandardSegment(String theName, int theIndex) throws HL7Exception { 236 if (this instanceof Message && theIndex == 0) { 237 throw new HL7Exception("Can not add nonstandard segment \"" + theName + "\" to start of message."); 238 } 239 240 String version = this.getMessage().getVersion(); 241 if (version == null) 242 throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null"); 243 Class<? extends Segment> c = myFactory.getSegmentClass(theName, version); 244 245 if (c == null) { 246 c = GenericSegment.class; 247 } 248 249 tryToInstantiateStructure(c, theName); // may throw exception 250 251 String newName = insert(c, false, true, theIndex, theName); 252 if (this.nonStandardNames == null) { 253 this.nonStandardNames = new HashSet<String>(); 254 } 255 this.nonStandardNames.add(newName); 256 257 return newName; 258 } 259 260 /** 261 * Returns a Set containing the names of all non-standard structures which have been added to 262 * this structure 263 * 264 * @return set of non-standard structures 265 */ 266 public Set<String> getNonStandardNames() { 267 if (nonStandardNames == null) { 268 return Collections.emptySet(); 269 } 270 return Collections.unmodifiableSet(nonStandardNames); 271 } 272 273 /** 274 * Returns an ordered array of the names of the Structures in this Group. These names can be 275 * used to iterate through the group using repeated calls to <code>get(name)</code>. 276 */ 277 public String[] getNames() { 278 String[] retVal = new String[this.names.size()]; 279 for (int i = 0; i < this.names.size(); i++) { 280 retVal[i] = this.names.get(i); 281 } 282 return retVal; 283 } 284 285 /** 286 * Adds a new Structure (group or segment) to this Group. A place for the Structure is added to 287 * the group but there are initially zero repetitions. This method should be used by the 288 * constructors of implementing classes to specify which Structures the Group contains - 289 * Structures should be added in the order in which they appear. Note that the class is supplied 290 * instead of an instance because we want there initially to be zero instances of each structure 291 * but we want the AbstractGroup code to be able to create instances as necessary to support 292 * get(...) calls. 293 * 294 * @return the actual name used to store this structure (may be appended with an integer if 295 * there are duplicates in the same Group). 296 */ 297 protected String add(Class<? extends Structure> c, boolean required, boolean repeating) throws HL7Exception { 298 return add(c, required, repeating, false); 299 } 300 301 /** 302 * Adds a new Structure (group or segment) to this Group. A place for the Structure is added to 303 * the group but there are initially zero repetitions. This method should be used by the 304 * constructors of implementing classes to specify which Structures the Group contains - 305 * Structures should be added in the order in which they appear. Note that the class is supplied 306 * instead of an instance because we want there initially to be zero instances of each structure 307 * but we want the AbstractGroup code to be able to create instances as necessary to support 308 * get(...) calls. 309 * 310 * @return the actual name used to store this structure (may be appended with an integer if 311 * there are duplicates in the same Group). 312 */ 313 protected String add(Class<? extends Structure> c, boolean required, boolean repeating, boolean choiceElement) throws HL7Exception { 314 String name = getName(c); 315 return insert(c, required, repeating, choiceElement, this.names.size(), name); 316 } 317 318 /** 319 * Adds a new Structure (group or segment) to this Group. A place for the Structure is added to 320 * the group but there are initially zero repetitions. This method should be used by the 321 * constructors of implementing classes to specify which Structures the Group contains - 322 * Structures should be added in the order in which they appear. Note that the class is supplied 323 * instead of an instance because we want there initially to be zero instances of each structure 324 * but we want the AbstractGroup code to be able to create instances as necessary to support 325 * get(...) calls. 326 * 327 * @return the actual name used to store this structure (may be appended with an integer if 328 * there are duplicates in the same Group). 329 */ 330 protected String add(Class<? extends Structure> c, boolean required, boolean repeating, int index) 331 throws HL7Exception { 332 String name = getName(c); 333 return insert(c, required, repeating, index, name); 334 } 335 336 /** 337 * Returns true if the class name is already being used. 338 */ 339 private boolean nameExists(String name) { 340 return this.classes.get(name) != null; 341 } 342 343 /** 344 * Attempts to create an instance of the given class and return it as a Structure. 345 * 346 * @param c the Structure implementing class 347 * @param name an optional name of the structure (used by Generic structures; may be null) 348 */ 349 protected Structure tryToInstantiateStructure(Class<? extends Structure> c, String name) throws HL7Exception { 350 if (GenericSegment.class.isAssignableFrom(c)) { 351 String genericName = name; 352 if (genericName.length() > 3) { 353 genericName = genericName.substring(0, 3); 354 } 355 return new GenericSegment(this, genericName); 356 } 357 if (GenericGroup.class.isAssignableFrom(c)) { 358 return new GenericGroup(this, name, myFactory); 359 } 360 try { 361 return ReflectionUtil.instantiateStructure(c, this, myFactory); 362 } catch (Exception e) { 363 return ReflectionUtil.instantiate(c); 364 } 365 366 } 367 368 /** 369 * {@inheritDoc} 370 */ 371 public boolean isChoiceElement(String theName) throws HL7Exception { 372 return choiceElements.contains(theName); 373 } 374 375 /** 376 * Returns true if the named structure is a group 377 */ 378 public boolean isGroup(String name) throws HL7Exception { 379 Class<? extends Structure> clazz = classes.get(name); 380 if (clazz == null) 381 throw new HL7Exception("The structure " + name + " does not exist in the group " 382 + this.getClass().getName()); 383 return Group.class.isAssignableFrom(clazz); 384 } 385 386 /** 387 * Returns true if the named structure is required. 388 */ 389 public boolean isRequired(String name) throws HL7Exception { 390 Boolean req = required.get(name); 391 if (req == null) 392 throw new HL7Exception("The structure " + name + " does not exist in the group " 393 + this.getClass().getName()); 394 return req; 395 } 396 397 /** 398 * Returns true if the named structure is required. 399 */ 400 public boolean isRepeating(String name) throws HL7Exception { 401 Boolean rep = repeating.get(name); 402 if (rep == null) 403 throw new HL7Exception("The structure " + name + " does not exist in the group " 404 + this.getClass().getName()); 405 return rep; 406 } 407 408 /** 409 * Returns the number of existing repetitions of the named structure. 410 * 411 * @param name structure name 412 * @return number of existing repetitions of the named structure 413 * @throws HL7Exception if the structure is unknown 414 */ 415 public int currentReps(String name) throws HL7Exception { 416 List<Structure> list = structures.get(name); 417 if (list == null) 418 throw new HL7Exception("The structure " + name + " does not exist in the group " 419 + this.getClass().getName()); 420 return list.size(); 421 } 422 423 /** 424 * Returns an array of Structure objects by name. For example, if the Group contains an MSH 425 * segment and "MSH" is supplied then this call would return a 1-element array containing the 426 * MSH segment. Multiple elements are returned when the segment or group repeats. The array may 427 * be empty if no repetitions have been accessed yet using the get(...) methods. 428 * 429 * @throws HL7Exception if the named Structure is not part of this Group. 430 */ 431 public Structure[] getAll(String name) throws HL7Exception { 432 List<Structure> list = structures.get(name); 433 if (list == null) { 434 throw new HL7Exception("The structure " + name + " does not exist in the group " 435 + this.getClass().getName()); 436 } 437 return list.toArray(new Structure[list.size()]); 438 } 439 440 /** 441 * Returns a list containing all existing repetitions of the structure identified by name 442 * 443 * @throws HL7Exception if the named Structure is not part of this Group. 444 */ 445 @SuppressWarnings("unchecked") 446 protected <T extends Structure> List<T> getAllAsList(String name, Class<T> theType) throws HL7Exception { 447 Class<? extends Structure> clazz = classes.get(name); 448 449 if (!theType.equals(clazz)) { 450 throw new HL7Exception("Structure with name \"" + name + "\" has type " + clazz.getName() 451 + " but should be " + theType); 452 } 453 List<T> retVal = new ArrayList<T>(); 454 for (Structure next : structures.get(name)) { 455 retVal.add((T) next); 456 } 457 return Collections.unmodifiableList(retVal); 458 } 459 460 /** 461 * Removes a repetition of a given Structure objects by name. For example, if the Group contains 462 * 10 repititions an OBX segment and "OBX" is supplied with an index of 2, then this call would 463 * remove the 3rd repetition. Note that in this case, the Set ID field in the OBX segments would 464 * also need to be renumbered manually. 465 * 466 * @param name structure name 467 * @param index repetition to remove the structure from 468 * @return The removed structure 469 * @throws HL7Exception if the named Structure is not part of this Group. 470 */ 471 public Structure removeRepetition(String name, int index) throws HL7Exception { 472 List<Structure> list = structures.get(name); 473 if (list == null) { 474 throw new HL7Exception("The structure " + name + " does not exist in the group " 475 + this.getClass().getName()); 476 } 477 if (list.size() == 0) { 478 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " has no repetitions"); 479 } 480 if (list.size() <= index) { 481 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and " 482 + (list.size() - 1)); 483 } 484 485 return list.remove(index); 486 } 487 488 /** 489 * Inserts a repetition of a given Structure into repetitions of that structure by name. For 490 * example, if the Group contains 10 repetitions an OBX segment and an OBX is supplied with an 491 * index of 2, then this call would insert the new repetition at index 2. (Note that in this 492 * example, the Set ID field in the OBX segments would also need to be renumbered manually). 493 * 494 * @throws HL7Exception if the named Structure is not part of this Group. 495 */ 496 protected void insertRepetition(String name, Structure structure, int index) throws HL7Exception { 497 if (structure == null) { 498 throw new NullPointerException("Structure may not be null"); 499 } 500 501 if (structure.getMessage() != this.getMessage()) { 502 throw new HL7Exception("Structure does not belong to this message"); 503 } 504 505 List<Structure> list = structures.get(name); 506 507 if (list == null) { 508 throw new HL7Exception("The structure " + name + " does not exist in the group " 509 + this.getClass().getName()); 510 } 511 if (list.size() < index) { 512 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and " 513 + (list.size())); 514 } 515 516 list.add(index, structure); 517 } 518 519 /** 520 * Inserts a repetition of a given Structure into repetitions of that structure by name. For 521 * example, if the Group contains 10 repititions an OBX segment and an OBX is supplied with an 522 * index of 2, then this call would insert the new repetition at index 2. Note that in this 523 * case, the Set ID field in the OBX segments would also need to be renumbered manually. 524 * 525 * @param name structure name 526 * @param index repetition to insert the structure 527 * @return The inserted structure 528 * @throws HL7Exception if the named Structure is not part of this Group. 529 */ 530 public Structure insertRepetition(String name, int index) throws HL7Exception { 531 if (name == null || name.length() == 0) { 532 throw new NullPointerException("Name may not be null/empty"); 533 } 534 535 Class<? extends Structure> structureClass = this.classes.get(name); 536 if (structureClass == null) { 537 throw new HL7Exception("Group " + this.getClass().getName() + " has no structure named " + name 538 + ": Valid names: " + this.classes.keySet()); 539 } 540 541 Structure rep = tryToInstantiateStructure(structureClass, name); 542 insertRepetition(name, rep, index); 543 544 return rep; 545 } 546 547 /** 548 * Given a child structure name, returns the child index (which is 1-indexed, meaning that the 549 * first child is at index 1 550 * 551 * @param name structure name 552 * @return position of the structure in this group 553 * @throws HL7Exception if the structure is unknown 554 */ 555 public int getFieldNumForName(String name) throws HL7Exception { 556 int retVal = names.indexOf(name); 557 if (retVal == -1) { 558 throw new HL7Exception("Unknown name: " + name); 559 } 560 return retVal + 1; 561 } 562 563 /** 564 * Returns the Class of the Structure at the given name index. 565 */ 566 public Class<? extends Structure> getClass(String name) { 567 return classes.get(name); 568 } 569 570 /** 571 * Returns the class name (excluding package). 572 * 573 * @see Structure#getName() 574 */ 575 public String getName() { 576 return getName(getClass()); 577 } 578 579 // returns a name for a class of a Structure in this Message 580 private String getName(Class<? extends Structure> c) { 581 String name = c.getSimpleName(); 582 if (Group.class.isAssignableFrom(c) && !Message.class.isAssignableFrom(c)) { 583 name = getGroupName(name); 584 } 585 return name; 586 } 587 588 /** 589 * Remove message name prefix from group names for compatibility with getters. Due to 590 * 3558962 we also need to look at the message's super classes to enable custom 591 * messages that reuse groups from their ancestors. 592 * 593 * @param name the simple name of the group 594 * @return the abbreviated group in name in case of matching prefixes 595 */ 596 private String getGroupName(String name) { 597 Class<?> messageClass = getMessage().getClass(); 598 while (Message.class.isAssignableFrom(messageClass)) { 599 @SuppressWarnings("unchecked") 600 // actually we should call getName() instead of getName(Class), but this 601 // is due to issue 3558962 602 String messageName = getName((Class<? extends Message>)messageClass); 603 if (name.startsWith(messageName) && name.length() > messageName.length()) { 604 return name.substring(messageName.length() + 1); 605 } 606 messageClass = messageClass.getSuperclass(); 607 } 608 return name; 609 } 610 611 612 613 /** 614 * Inserts the given structure into this group, at the indicated index number. This method is 615 * used to support handling of unexpected segments (e.g. Z-segments). In contrast, specification 616 * of the group's normal children should be done at construction time, using the add(...) 617 * method. 618 */ 619 protected String insert(Class<? extends Structure> c, boolean required, boolean repeating, int index, String name) 620 throws HL7Exception { 621 return insert(c, required, repeating, false, index, name); 622 } 623 624 protected String insert(Class<? extends Structure> c, boolean required, boolean repeating, boolean choiceElement, 625 int index, String name) throws HL7Exception { 626 // tryToInstantiateStructure(c, name); //may throw exception 627 628 // see if there is already something by this name and make a new name if 629 // necessary ... 630 if (nameExists(name)) { 631 int version = 2; 632 String newName = name; 633 while (nameExists(newName)) { 634 newName = name + version++; 635 } 636 name = newName; 637 } 638 639 if (index > this.names.size()) { 640 throw new HL7Exception("Invalid index " + index + " - Should be <= " + this.names.size()); 641 } 642 643 this.names.add(index, name); 644 this.required.put(name, required); 645 this.repeating.put(name, repeating); 646 this.classes.put(name, c); 647 this.structures.put(name, new ArrayList<Structure>()); 648 649 if (choiceElement) { 650 this.choiceElements.add(name); 651 } 652 653 return name; 654 } 655 656 /** 657 * Clears all data from this structure. 658 */ 659 public void clear() { 660 for (List<Structure> next : structures.values()) { 661 if (next != null) { 662 next.clear(); 663 } 664 } 665 } 666 667 /** 668 * Returns the {@link ModelClassFactory} associated with this structure 669 * 670 * @return the {@link ModelClassFactory} associated with this structure 671 */ 672 public final ModelClassFactory getModelClassFactory() { 673 return myFactory; 674 } 675 676 /** 677 * Iterates over the contained structures and calls the visitor for each 678 * of them. 679 * 680 * @param visitor MessageVisitor instance to be called back. 681 * @param location location of the group 682 * @return true if visiting shall continue, false if not 683 * @throws HL7Exception 684 */ 685 public boolean accept(MessageVisitor visitor, Location location) throws HL7Exception { 686 if (visitor.start(this, location)) { 687 visitNestedStructures(visitor, location); 688 } 689 return visitor.end(this, location); 690 } 691 692 public Location provideLocation(Location location, int index, int repetition) { 693 return new Location(location).pushGroup(getName(), repetition); 694 } 695 696 protected void visitNestedStructures(MessageVisitor visitor, Location location) throws HL7Exception { 697 for (String name : getNames()) { 698 Structure[] structures = getAll(name); 699 for (int j=0; j < structures.length; j++) { 700 int rep = isRepeating(name) ? j : -1; 701 Location nextLocation = structures[j].provideLocation(location, -1, rep); 702 if (!structures[j].accept(visitor, nextLocation)) break; 703 } 704 } 705 } 706 707 /** 708 * <p> 709 * Appends a description of this group's structure and all children's structure to a string 710 * builder. 711 * </p> 712 * <p> 713 * Note that this method is intended only to be called by 714 * {@link AbstractMessage#printStructure()}. Please use caution if calling this method directly, 715 * as the method signature and/or behaviour may change in the future. 716 * </p> 717 */ 718 void appendStructureDescription(StringBuilder theStringBuilder, int theIndent, boolean theOptional, 719 boolean theRepeating, boolean theAddStartName, boolean theAddEndName, boolean thePrintEmpty) throws HL7Exception { 720 String lineSeparator = System.getProperty("line.separator"); 721 722 if (theAddStartName) { 723 indent(theStringBuilder, theIndent); 724 theStringBuilder.append(getName()).append(" (start)").append(lineSeparator); 725 } 726 727 if (theOptional || theRepeating) { 728 indent(theStringBuilder, theIndent); 729 if (theOptional) { 730 theStringBuilder.append("["); 731 } 732 if (theRepeating) { 733 theStringBuilder.append("{"); 734 } 735 theStringBuilder.append(lineSeparator); 736 } 737 738 boolean inChoice = false; 739 740 for (String nextName : getNames()) { 741 742 if (!thePrintEmpty) { 743 boolean hasContent = false; 744 Structure[] allReps = getAll(nextName); 745 for (Structure structure : allReps) { 746 if (!structure.isEmpty()) { 747 hasContent = true; 748 break; 749 } 750 } 751 752 if (!hasContent) { 753 continue; 754 } 755 } 756 757 Class<? extends Structure> nextClass = classes.get(nextName); 758 759 boolean nextOptional = !isRequired(nextName); 760 boolean nextRepeating = isRepeating(nextName); 761 boolean nextChoice = isChoiceElement(nextName); 762 763 if (nextChoice && !inChoice) { 764 theIndent += PS_INDENT; 765 indent(theStringBuilder, theIndent); 766 theStringBuilder.append("<"); 767 theStringBuilder.append(lineSeparator); 768 inChoice = true; 769 } else if (!nextChoice && inChoice) { 770 indent(theStringBuilder, theIndent); 771 theStringBuilder.append(">"); 772 theStringBuilder.append(lineSeparator); 773 inChoice = false; 774 theIndent -= PS_INDENT; 775 } else if (nextChoice && inChoice) { 776 indent(theStringBuilder, theIndent); 777 theStringBuilder.append("|"); 778 theStringBuilder.append(lineSeparator); 779 } 780 781 if (AbstractGroup.class.isAssignableFrom(nextClass)) { 782 783 Structure[] nextChildren = getAll(nextName); 784 for (int i = 0; i < nextChildren.length; i++) { 785 786 Structure structure = nextChildren[i]; 787 boolean addStartName = (i == 0); 788 boolean addEndName = (i == (nextChildren.length - 1)); 789 ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT, 790 nextOptional, nextRepeating, addStartName, addEndName, thePrintEmpty); 791 792 } 793 794 if (nextChildren.length == 0) { 795 Structure structure = tryToInstantiateStructure(nextClass, nextName); 796 ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT, 797 nextOptional, nextRepeating, true, true, thePrintEmpty); 798 } 799 800 } else if (Segment.class.isAssignableFrom(nextClass)) { 801 802 int currentIndent = theStringBuilder.length(); 803 804 StringBuilder structurePrefix = new StringBuilder(); 805 indent(structurePrefix, theIndent + PS_INDENT); 806 if (nextOptional) { 807 structurePrefix.append("[ "); 808 } 809 if (nextRepeating) { 810 structurePrefix.append("{ "); 811 } 812 structurePrefix.append(nextName); 813 if (nextRepeating) { 814 structurePrefix.append(" }"); 815 } 816 if (nextOptional) { 817 structurePrefix.append(" ]"); 818 } 819 820 if (this.nonStandardNames != null && this.nonStandardNames.contains(nextName)) { 821 structurePrefix.append(" (non-standard)"); 822 } 823 structurePrefix.append(" - "); 824 825 currentIndent = theStringBuilder.length() - currentIndent; 826 List<Structure> nextStructureList = structures.get(nextName); 827 theStringBuilder.append(structurePrefix); 828 if (nextStructureList == null || nextStructureList.isEmpty()) { 829 theStringBuilder.append("Not populated"); 830 theStringBuilder.append(lineSeparator); 831 } else { 832 for (int i = 0; i < nextStructureList.size(); i++) { 833 if (i > 0) { 834 indent(theStringBuilder, currentIndent + structurePrefix.length()); 835 } 836 Segment nextSegment = (Segment) nextStructureList.get(i); 837 theStringBuilder.append(new PipeParser().doEncode(nextSegment, 838 EncodingCharacters.getInstance(getMessage()))); 839 theStringBuilder.append(lineSeparator); 840 841 } 842 } 843 844 } 845 } 846 847 if (inChoice) { 848 indent(theStringBuilder, theIndent); 849 theStringBuilder.append(">"); 850 theStringBuilder.append(lineSeparator); 851 theIndent -= PS_INDENT; 852 } 853 854 if (theOptional || theRepeating) { 855 indent(theStringBuilder, theIndent); 856 if (theRepeating) { 857 theStringBuilder.append("}"); 858 } 859 if (theOptional) { 860 theStringBuilder.append("]"); 861 } 862 theStringBuilder.append(lineSeparator); 863 } 864 865 if (theAddEndName) { 866 indent(theStringBuilder, theIndent); 867 theStringBuilder.append(getName()).append(" (end)").append(lineSeparator); 868 } 869 } 870 871 private void indent(StringBuilder theStringBuilder, int theIndent) { 872 for (int i = 0; i < theIndent; i++) { 873 theStringBuilder.append(' '); 874 } 875 } 876}