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.VersionLogger; 040import ca.uhn.hl7v2.parser.EncodingCharacters; 041import ca.uhn.hl7v2.parser.ModelClassFactory; 042import ca.uhn.hl7v2.parser.PipeParser; 043import ca.uhn.hl7v2.util.ReflectionUtil; 044 045/** 046 * A partial implementation of Group. Subclasses correspond to specific groups of segments (and/or 047 * other sub-groups) that are implicitly defined by message structures in the HL7 specification. A 048 * subclass should define it's group structure by putting repeated calls to the add(...) method in 049 * it's constructor. Each call to add(...) adds a specific component to the Group. 050 * 051 * @author Bryan Tripp (bryan_tripp@sourceforge.net) 052 */ 053public abstract class AbstractGroup extends AbstractStructure implements Group { 054 055 private static final int PS_INDENT = 3; 056 057 private static final long serialVersionUID = 1772720246448224363L; 058 059 private List<String> names; 060 private Map<String, List<Structure>> structures; 061 private Map<String, Boolean> required; 062 private Map<String, Boolean> repeating; 063 private Set<String> choiceElements; 064 private Map<String, Class<? extends Structure>> classes; 065 // protected Message message; 066 private Set<String> nonStandardNames; 067 private final ModelClassFactory myFactory; 068 069 static { 070 VersionLogger.init(); 071 } 072 073 /** 074 * This constructor should be used by implementing classes that do not also implement Message. 075 * 076 * @param parent the group to which this Group belongs. 077 * @param factory the factory for classes of segments, groups, and datatypes under this group 078 */ 079 protected AbstractGroup(Group parent, ModelClassFactory factory) { 080 super(parent); 081 this.myFactory = factory; 082 init(); 083 } 084 085 private void init() { 086 names = new ArrayList<String>(); 087 structures = new HashMap<String, List<Structure>>(); 088 required = new HashMap<String, Boolean>(); 089 repeating = new HashMap<String, Boolean>(); 090 classes = new HashMap<String, Class<? extends Structure>>(); 091 choiceElements = new HashSet<String>(); 092 } 093 094 /** 095 * Returns the named structure. If this Structure is repeating then the first repetition is 096 * returned. Creates the Structure if necessary. 097 * 098 * @throws HL7Exception if the named Structure is not part of this Group. 099 */ 100 public Structure get(String name) throws HL7Exception { 101 return get(name, 0); 102 } 103 104 protected <T extends Structure> T getTyped(String name, Class<T> type) { 105 try { 106 @SuppressWarnings("unchecked") 107 T ret = (T) get(name); 108 return ret; 109 } catch (HL7Exception e) { 110 log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e); 111 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 List<Structure> list = structures.get(name); 125 if (list == null) 126 throw new HL7Exception(name + " does not exist in the group " + this.getClass().getName()); 127 128 Structure ret; 129 if (rep < list.size()) { 130 // return existing Structure if it exists 131 ret = list.get(rep); 132 } else if (rep == list.size()) { 133 // verify that Structure is repeating ... 134 Boolean repeats = this.repeating.get(name); 135 if (!repeats && list.size() > 0) 136 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 Class<? extends Structure> c = classes.get(name); // get class 141 ret = tryToInstantiateStructure(c, name); 142 list.add(ret); 143 } else { 144 StringBuilder b = new StringBuilder(); 145 b.append("Can't return repetition #"); 146 b.append(rep); 147 b.append(" of "); 148 b.append(name); 149 b.append(" - there are currently "); 150 if (list.size() == 0) { 151 b.append("no"); 152 } else { 153 b.append("only "); 154 b.append(list.size()); 155 } 156 b.append(" repetitions "); 157 b.append("so rep# must be "); 158 if (list.size() == 0) { 159 b.append("0"); 160 } else { 161 b.append("between 0 and "); 162 b.append(list.size()); 163 } 164 throw new HL7Exception(b.toString()); 165 } 166 return ret; 167 } 168 169 /** 170 * {@inheritDoc} 171 */ 172 public boolean isEmpty() throws HL7Exception { 173 for (String name : getNames()) { 174 if (!get(name).isEmpty()) 175 return false; 176 } 177 return true; 178 } 179 180 protected <T extends Structure> T getTyped(String name, int rep, Class<T> type) { 181 try { 182 @SuppressWarnings("unchecked") 183 T ret = (T) get(name, rep); 184 return ret; 185 } catch (HL7Exception e) { 186 List<Structure> list = structures.get(name); 187 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 log.error("Unexpected error accessing data - this is probably a bug in the source code generator.", e); 191 } 192 throw new RuntimeException(e); 193 } 194 } 195 196 protected int getReps(String name) { 197 try { 198 return getAll(name).length; 199 } catch (HL7Exception e) { 200 String message = "Unexpected error accessing data - this is probably a bug in the source code generator."; 201 log.error(message, e); 202 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 String version = this.getMessage().getVersion(); 215 if (version == null) 216 throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null"); 217 Class<? extends Segment> c = myFactory.getSegmentClass(name, version); 218 if (c == null) 219 c = GenericSegment.class; 220 221 int index = this.getNames().length; 222 223 tryToInstantiateStructure(c, name); // may throw exception 224 225 String newName = insert(c, false, true, index, name); 226 if (this.nonStandardNames == null) { 227 this.nonStandardNames = new HashSet<String>(); 228 } 229 this.nonStandardNames.add(newName); 230 231 return newName; 232 } 233 234 public String addNonstandardSegment(String theName, int theIndex) throws HL7Exception { 235 if (this instanceof Message && theIndex == 0) { 236 throw new HL7Exception("Can not add nonstandard segment \"" + theName + "\" to start of message."); 237 } 238 239 String version = this.getMessage().getVersion(); 240 if (version == null) 241 throw new HL7Exception("Need message version to add segment by name; message.getVersion() returns null"); 242 Class<? extends Segment> c = myFactory.getSegmentClass(theName, version); 243 244 if (c == null) { 245 c = GenericSegment.class; 246 } 247 248 tryToInstantiateStructure(c, theName); // may throw exception 249 250 String newName = insert(c, false, true, theIndex, theName); 251 if (this.nonStandardNames == null) { 252 this.nonStandardNames = new HashSet<String>(); 253 } 254 this.nonStandardNames.add(newName); 255 256 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 if (nonStandardNames == null) { 265 return Collections.emptySet(); 266 } 267 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 String[] retVal = new String[this.names.size()]; 276 for (int i = 0; i < this.names.size(); i++) { 277 retVal[i] = this.names.get(i); 278 } 279 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 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 String name = getName(c); 312 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 String name = getName(c); 330 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 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 if (GenericSegment.class.isAssignableFrom(c)) { 348 String genericName = name; 349 if (genericName.length() > 3) { 350 genericName = genericName.substring(0, 3); 351 } 352 return new GenericSegment(this, genericName); 353 } 354 if (GenericGroup.class.isAssignableFrom(c)) { 355 return new GenericGroup(this, name, myFactory); 356 } 357 try { 358 return ReflectionUtil.instantiateStructure(c, this, myFactory); 359 } catch (Exception e) { 360 return ReflectionUtil.instantiate(c); 361 } 362 363 } 364 365 /** 366 * {@inheritDoc} 367 */ 368 public boolean isChoiceElement(String theName) throws HL7Exception { 369 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 Class<? extends Structure> clazz = classes.get(name); 377 if (clazz == null) 378 throw new HL7Exception("The structure " + name + " does not exist in the group " 379 + this.getClass().getName()); 380 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 Boolean req = required.get(name); 388 if (req == null) 389 throw new HL7Exception("The structure " + name + " does not exist in the group " 390 + this.getClass().getName()); 391 return req; 392 } 393 394 /** 395 * Returns true if the named structure is required. 396 */ 397 public boolean isRepeating(String name) throws HL7Exception { 398 Boolean rep = repeating.get(name); 399 if (rep == null) 400 throw new HL7Exception("The structure " + name + " does not exist in the group " 401 + this.getClass().getName()); 402 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 List<Structure> list = structures.get(name); 410 if (list == null) 411 throw new HL7Exception("The structure " + name + " does not exist in the group " 412 + this.getClass().getName()); 413 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 List<Structure> list = structures.get(name); 426 if (list == null) { 427 throw new HL7Exception("The structure " + name + " does not exist in the group " 428 + this.getClass().getName()); 429 } 430 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 Class<? extends Structure> clazz = classes.get(name); 441 442 if (!theType.equals(clazz)) { 443 throw new HL7Exception("Structure with name \"" + name + "\" has type " + clazz.getName() 444 + " but should be " + theType); 445 } 446 List<T> retVal = new ArrayList<T>(); 447 for (Structure next : structures.get(name)) { 448 retVal.add((T) next); 449 } 450 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 List<Structure> list = structures.get(name); 464 if (list == null) { 465 throw new HL7Exception("The structure " + name + " does not exist in the group " 466 + this.getClass().getName()); 467 } 468 if (list.size() == 0) { 469 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " has no repetitions"); 470 } 471 if (list.size() <= index) { 472 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and " 473 + (list.size() - 1)); 474 } 475 476 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 if (structure == null) { 489 throw new NullPointerException("Structure may not be null"); 490 } 491 492 if (structure.getMessage() != this.getMessage()) { 493 throw new HL7Exception("Structure does not belong to this message"); 494 } 495 496 List<Structure> list = structures.get(name); 497 498 if (list == null) { 499 throw new HL7Exception("The structure " + name + " does not exist in the group " 500 + this.getClass().getName()); 501 } 502 if (list.size() < index) { 503 throw new HL7Exception("Invalid index: " + index + ", structure " + name + " must be between 0 and " 504 + (list.size())); 505 } 506 507 list.add(index, structure); 508 } 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 if (name == null || name.length() == 0) { 521 throw new NullPointerException("Name may not be null/empty"); 522 } 523 524 Class<? extends Structure> structureClass = this.classes.get(name); 525 if (structureClass == null) { 526 throw new HL7Exception("Group " + this.getClass().getName() + " has no structure named " + name 527 + ": Valid names: " + this.classes.keySet()); 528 } 529 530 Structure rep = tryToInstantiateStructure(structureClass, name); 531 insertRepetition(name, rep, index); 532 533 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 int retVal = names.indexOf(name); 542 if (retVal == -1) { 543 throw new HL7Exception("Unknown name: " + name); 544 } 545 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 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 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 String name = c.getSimpleName(); 567 if (Group.class.isAssignableFrom(c) && !Message.class.isAssignableFrom(c)) { 568 name = getGroupName(name); 569 } 570 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 Class<?> messageClass = getMessage().getClass(); 583 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 String messageName = getName((Class<? extends Message>)messageClass); 588 if (name.startsWith(messageName) && name.length() > messageName.length()) { 589 return name.substring(messageName.length() + 1); 590 } 591 messageClass = messageClass.getSuperclass(); 592 } 593 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 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 if (nameExists(name)) { 616 int version = 2; 617 String newName = name; 618 while (nameExists(newName)) { 619 newName = name + version++; 620 } 621 name = newName; 622 } 623 624 if (index > this.names.size()) { 625 throw new HL7Exception("Invalid index " + index + " - Should be <= " + this.names.size()); 626 } 627 628 this.names.add(index, name); 629 this.required.put(name, new Boolean(required)); 630 this.repeating.put(name, new Boolean(repeating)); 631 this.classes.put(name, c); 632 this.structures.put(name, new ArrayList<Structure>()); 633 634 if (choiceElement) { 635 this.choiceElements.add(name); 636 } 637 638 return name; 639 } 640 641 /** 642 * Clears all data from this structure. 643 */ 644 public void clear() { 645 for (List<Structure> next : structures.values()) { 646 if (next != null) { 647 next.clear(); 648 } 649 } 650 } 651 652 /** 653 * Returns the {@link ModelClassFactory} associated with this structure 654 */ 655 public final ModelClassFactory getModelClassFactory() { 656 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 String lineSeparator = System.getProperty("line.separator"); 673 674 if (theAddStartName) { 675 indent(theStringBuilder, theIndent); 676 theStringBuilder.append(getName()).append(" (start)").append(lineSeparator); 677 } 678 679 if (theOptional || theRepeating) { 680 indent(theStringBuilder, theIndent); 681 if (theOptional) { 682 theStringBuilder.append("["); 683 } 684 if (theRepeating) { 685 theStringBuilder.append("{"); 686 } 687 theStringBuilder.append(lineSeparator); 688 } 689 690 boolean inChoice = false; 691 692 for (String nextName : getNames()) { 693 694 if (!thePrintEmpty) { 695 boolean hasContent = false; 696 Structure[] allReps = getAll(nextName); 697 for (Structure structure : allReps) { 698 if (!structure.isEmpty()) { 699 hasContent = true; 700 break; 701 } 702 } 703 704 if (!hasContent) { 705 continue; 706 } 707 } 708 709 Class<? extends Structure> nextClass = classes.get(nextName); 710 711 boolean nextOptional = !isRequired(nextName); 712 boolean nextRepeating = isRepeating(nextName); 713 boolean nextChoice = isChoiceElement(nextName); 714 715 if (nextChoice && !inChoice) { 716 theIndent += PS_INDENT; 717 indent(theStringBuilder, theIndent); 718 theStringBuilder.append("<"); 719 theStringBuilder.append(lineSeparator); 720 inChoice = true; 721 } else if (!nextChoice && inChoice) { 722 indent(theStringBuilder, theIndent); 723 theStringBuilder.append(">"); 724 theStringBuilder.append(lineSeparator); 725 inChoice = false; 726 theIndent -= PS_INDENT; 727 } else if (nextChoice && inChoice) { 728 indent(theStringBuilder, theIndent); 729 theStringBuilder.append("|"); 730 theStringBuilder.append(lineSeparator); 731 } 732 733 if (AbstractGroup.class.isAssignableFrom(nextClass)) { 734 735 Structure[] nextChildren = getAll(nextName); 736 for (int i = 0; i < nextChildren.length; i++) { 737 738 Structure structure = nextChildren[i]; 739 boolean addStartName = (i == 0); 740 boolean addEndName = (i == (nextChildren.length - 1)); 741 ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT, 742 nextOptional, nextRepeating, addStartName, addEndName, thePrintEmpty); 743 744 } 745 746 if (nextChildren.length == 0) { 747 Structure structure = tryToInstantiateStructure(nextClass, nextName); 748 ((AbstractGroup) structure).appendStructureDescription(theStringBuilder, theIndent + PS_INDENT, 749 nextOptional, nextRepeating, true, true, thePrintEmpty); 750 } 751 752 } else if (Segment.class.isAssignableFrom(nextClass)) { 753 754 int currentIndent = theStringBuilder.length(); 755 756 StringBuilder structurePrefix = new StringBuilder(); 757 indent(structurePrefix, theIndent + PS_INDENT); 758 if (nextOptional) { 759 structurePrefix.append("[ "); 760 } 761 if (nextRepeating) { 762 structurePrefix.append("{ "); 763 } 764 structurePrefix.append(nextName); 765 if (nextRepeating) { 766 structurePrefix.append(" }"); 767 } 768 if (nextOptional) { 769 structurePrefix.append(" ]"); 770 } 771 772 if (this.nonStandardNames != null && this.nonStandardNames.contains(nextName)) { 773 structurePrefix.append(" (non-standard)"); 774 } 775 structurePrefix.append(" - "); 776 777 currentIndent = theStringBuilder.length() - currentIndent; 778 List<Structure> nextStructureList = structures.get(nextName); 779 theStringBuilder.append(structurePrefix); 780 if (nextStructureList == null || nextStructureList.isEmpty()) { 781 theStringBuilder.append("Not populated"); 782 theStringBuilder.append(lineSeparator); 783 } else { 784 for (int i = 0; i < nextStructureList.size(); i++) { 785 if (i > 0) { 786 indent(theStringBuilder, currentIndent + structurePrefix.length()); 787 } 788 Segment nextSegment = (Segment) nextStructureList.get(i); 789 theStringBuilder.append(new PipeParser().doEncode(nextSegment, 790 EncodingCharacters.getInstance(getMessage()))); 791 theStringBuilder.append(lineSeparator); 792 793 } 794 } 795 796 } 797 } 798 799 if (inChoice) { 800 indent(theStringBuilder, theIndent); 801 theStringBuilder.append(">"); 802 theStringBuilder.append(lineSeparator); 803 theIndent -= PS_INDENT; 804 } 805 806 if (theOptional || theRepeating) { 807 indent(theStringBuilder, theIndent); 808 if (theRepeating) { 809 theStringBuilder.append("}"); 810 } 811 if (theOptional) { 812 theStringBuilder.append("]"); 813 } 814 theStringBuilder.append(lineSeparator); 815 } 816 817 if (theAddEndName) { 818 indent(theStringBuilder, theIndent); 819 theStringBuilder.append(getName()).append(" (end)").append(lineSeparator); 820 } 821 } 822 823 private void indent(StringBuilder theStringBuilder, int theIndent) { 824 for (int i = 0; i < theIndent; i++) { 825 theStringBuilder.append(' '); 826 } 827 } 828}