
001package org.hl7.fhir.r4.elementmodel; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032import java.io.BufferedInputStream; 033import java.io.IOException; 034import java.io.InputStream; 035import java.io.InputStreamReader; 036import java.io.OutputStream; 037 038import org.hl7.fhir.exceptions.DefinitionException; 039import org.hl7.fhir.exceptions.FHIRException; 040import org.hl7.fhir.exceptions.FHIRFormatError; 041import org.hl7.fhir.r4.context.IWorkerContext; 042import org.hl7.fhir.r4.formats.IParser.OutputStyle; 043import org.hl7.fhir.r4.model.StructureDefinition; 044 045/** 046 * This class provides special support for parsing v2 by the v2 logical model 047 * For the logical model, see the FHIRPath spec 048 * 049 * @author Grahame Grieve 050 * 051 */ 052@Deprecated 053public class VerticalBarParser extends ParserBase { 054 055 /** 056 * Delimiters for a message. Note that the application rarely needs to concern 057 * itself with this information; it mainly exists for internal use. However if a 058 * message is being written to a spec that calls for non-standard delimiters, 059 * the application can set them here. 060 * 061 * @author Grahame 062 * 063 */ 064 public class Delimiters { 065 066 /** 067 * Hl7 defined default delimiter for a field 068 */ 069 public final static char DEFAULT_DELIMITER_FIELD = '|'; 070 071 /** 072 * Hl7 defined default delimiter for a component 073 */ 074 public final static char DEFAULT_DELIMITER_COMPONENT = '^'; 075 076 /** 077 * Hl7 defined default delimiter for a subcomponent 078 */ 079 public final static char DEFAULT_DELIMITER_SUBCOMPONENT = '&'; 080 081 /** 082 * Hl7 defined default delimiter for a repeat 083 */ 084 public final static char DEFAULT_DELIMITER_REPETITION = '~'; 085 086 /** 087 * Hl7 defined default delimiter for an escape 088 */ 089 public final static char DEFAULT_CHARACTER_ESCAPE = '\\'; 090 091 /** 092 * defined escape character for this message 093 */ 094 private char escapeCharacter; 095 096 /** 097 * defined repetition character for this message 098 */ 099 private char repetitionDelimiter; 100 101 /** 102 * defined field character for this message 103 */ 104 private char fieldDelimiter; 105 106 /** 107 * defined subComponent character for this message 108 */ 109 private char subComponentDelimiter; 110 111 /** 112 * defined component character for this message 113 */ 114 private char componentDelimiter; 115 116 /** 117 * create 118 * 119 */ 120 public Delimiters() { 121 super(); 122 reset(); 123 } 124 125 public boolean matches(Delimiters other) { 126 return escapeCharacter == other.escapeCharacter && repetitionDelimiter == other.repetitionDelimiter 127 && fieldDelimiter == other.fieldDelimiter && subComponentDelimiter == other.subComponentDelimiter 128 && componentDelimiter == other.componentDelimiter; 129 } 130 131 /** 132 * get defined component character for this message 133 * 134 * @return 135 */ 136 public char getComponentDelimiter() { 137 return componentDelimiter; 138 } 139 140 /** 141 * set defined component character for this message 142 * 143 * @param componentDelimiter 144 */ 145 public void setComponentDelimiter(char componentDelimiter) { 146 this.componentDelimiter = componentDelimiter; 147 } 148 149 /** 150 * get defined escape character for this message 151 * 152 * @return 153 */ 154 public char getEscapeCharacter() { 155 return escapeCharacter; 156 } 157 158 /** 159 * set defined escape character for this message 160 * 161 * @param escapeCharacter 162 */ 163 public void setEscapeCharacter(char escapeCharacter) { 164 this.escapeCharacter = escapeCharacter; 165 } 166 167 /** 168 * get defined field character for this message 169 * 170 * @return 171 */ 172 public char getFieldDelimiter() { 173 return fieldDelimiter; 174 } 175 176 /** 177 * set defined field character for this message 178 * 179 * @param fieldDelimiter 180 */ 181 public void setFieldDelimiter(char fieldDelimiter) { 182 this.fieldDelimiter = fieldDelimiter; 183 } 184 185 /** 186 * get repeat field character for this message 187 * 188 * @return 189 */ 190 public char getRepetitionDelimiter() { 191 return repetitionDelimiter; 192 } 193 194 /** 195 * set repeat field character for this message 196 * 197 * @param repetitionDelimiter 198 */ 199 public void setRepetitionDelimiter(char repetitionDelimiter) { 200 this.repetitionDelimiter = repetitionDelimiter; 201 } 202 203 /** 204 * get sub-component field character for this message 205 * 206 * @return 207 */ 208 public char getSubComponentDelimiter() { 209 return subComponentDelimiter; 210 } 211 212 /** 213 * set sub-component field character for this message 214 * 215 * @param subComponentDelimiter 216 */ 217 public void setSubComponentDelimiter(char subComponentDelimiter) { 218 this.subComponentDelimiter = subComponentDelimiter; 219 } 220 221 /** 222 * reset to default HL7 values 223 * 224 */ 225 public void reset() { 226 fieldDelimiter = DEFAULT_DELIMITER_FIELD; 227 componentDelimiter = DEFAULT_DELIMITER_COMPONENT; 228 subComponentDelimiter = DEFAULT_DELIMITER_SUBCOMPONENT; 229 repetitionDelimiter = DEFAULT_DELIMITER_REPETITION; 230 escapeCharacter = DEFAULT_CHARACTER_ESCAPE; 231 } 232 233 /** 234 * check that the delimiters are valid 235 * 236 * @throws FHIRException 237 */ 238 public void check() throws FHIRException { 239 rule(componentDelimiter != fieldDelimiter, 240 "Delimiter Error: \"" + componentDelimiter + "\" is used for both CPComponent and CPField"); 241 rule(subComponentDelimiter != fieldDelimiter, 242 "Delimiter Error: \"" + subComponentDelimiter + "\" is used for both CPSubComponent and CPField"); 243 rule(subComponentDelimiter != componentDelimiter, 244 "Delimiter Error: \"" + subComponentDelimiter + "\" is used for both CPSubComponent and CPComponent"); 245 rule(repetitionDelimiter != fieldDelimiter, 246 "Delimiter Error: \"" + repetitionDelimiter + "\" is used for both Repetition and CPField"); 247 rule(repetitionDelimiter != componentDelimiter, 248 "Delimiter Error: \"" + repetitionDelimiter + "\" is used for both Repetition and CPComponent"); 249 rule(repetitionDelimiter != subComponentDelimiter, 250 "Delimiter Error: \"" + repetitionDelimiter + "\" is used for both Repetition and CPSubComponent"); 251 rule(escapeCharacter != fieldDelimiter, 252 "Delimiter Error: \"" + escapeCharacter + "\" is used for both Escape and CPField"); 253 rule(escapeCharacter != componentDelimiter, 254 "Delimiter Error: \"" + escapeCharacter + "\" is used for both Escape and CPComponent"); 255 rule(escapeCharacter != subComponentDelimiter, 256 "Delimiter Error: \"" + escapeCharacter + "\" is used for both Escape and CPSubComponent"); 257 rule(escapeCharacter != repetitionDelimiter, 258 "Delimiter Error: \"" + escapeCharacter + "\" is used for both Escape and Repetition"); 259 } 260 261 /** 262 * check to see whether ch is a delimiter character (vertical bar parser 263 * support) 264 * 265 * @param ch 266 * @return 267 */ 268 public boolean isDelimiter(char ch) { 269 return ch == escapeCharacter || ch == repetitionDelimiter || ch == fieldDelimiter || ch == subComponentDelimiter 270 || ch == componentDelimiter; 271 } 272 273 /** 274 * check to see whether ch is a cell delimiter char (vertical bar parser 275 * support) 276 * 277 * @param ch 278 * @return 279 */ 280 public boolean isCellDelimiter(char ch) { 281 return ch == repetitionDelimiter || ch == fieldDelimiter || ch == subComponentDelimiter 282 || ch == componentDelimiter; 283 } 284 285 /** 286 * get the escape for a character 287 * 288 * @param ch 289 * @return 290 */ 291 public String getEscape(char ch) { 292 if (ch == escapeCharacter) 293 return escapeCharacter + "E" + escapeCharacter; 294 else if (ch == fieldDelimiter) 295 return escapeCharacter + "F" + escapeCharacter; 296 else if (ch == componentDelimiter) 297 return escapeCharacter + "S" + escapeCharacter; 298 else if (ch == subComponentDelimiter) 299 return escapeCharacter + "T" + escapeCharacter; 300 else if (ch == repetitionDelimiter) 301 return escapeCharacter + "R" + escapeCharacter; 302 else 303 return null; 304 } 305 306 /** 307 * build the MSH-2 content 308 * 309 * @return 310 */ 311 public String forMSH2() { 312 return "" + componentDelimiter + repetitionDelimiter + escapeCharacter + subComponentDelimiter; 313 } 314 315 /** 316 * check to see whether ch represents a delimiter escape 317 * 318 * @param ch 319 * @return 320 */ 321 public boolean isDelimiterEscape(char ch) { 322 return ch == 'F' || ch == 'S' || ch == 'E' || ch == 'T' || ch == 'R'; 323 } 324 325 /** 326 * get escape for ch in an escape 327 * 328 * @param ch 329 * @return 330 * @throws DefinitionException 331 * @throws FHIRException 332 */ 333 public char getDelimiterEscapeChar(char ch) throws DefinitionException { 334 if (ch == 'E') 335 return escapeCharacter; 336 else if (ch == 'F') 337 return fieldDelimiter; 338 else if (ch == 'S') 339 return componentDelimiter; 340 else if (ch == 'T') 341 return subComponentDelimiter; 342 else if (ch == 'R') 343 return repetitionDelimiter; 344 else 345 throw new DefinitionException("internal error in getDelimiterEscapeChar"); 346 } 347 } 348 349 public class VerticalBarParserReader { 350 351 private BufferedInputStream stream; 352 private String charsetName; 353 private InputStreamReader reader = null; 354 private boolean finished; 355 private char peeked; 356 private char lastValue; 357 private int offset; 358 private int lineNumber; 359 360 public VerticalBarParserReader(BufferedInputStream stream, String charsetName) throws FHIRException { 361 super(); 362 setStream(stream); 363 setCharsetName(charsetName); 364 open(); 365 } 366 367 public String getCharsetName() { 368 return charsetName; 369 } 370 371 public void setCharsetName(String charsetName) { 372 this.charsetName = charsetName; 373 } 374 375 public BufferedInputStream getStream() { 376 return stream; 377 } 378 379 public void setStream(BufferedInputStream stream) { 380 this.stream = stream; 381 } 382 383 private void open() throws FHIRException { 384 try { 385 stream.mark(2048); 386 reader = new InputStreamReader(stream, charsetName); 387 offset = 0; 388 lineNumber = 0; 389 lastValue = ' '; 390 next(); 391 } catch (Exception e) { 392 throw new FHIRException(e); 393 } 394 } 395 396 private void next() throws IOException, FHIRException { 397 finished = !reader.ready(); 398 if (!finished) { 399 char[] temp = new char[1]; 400 rule(reader.read(temp, 0, 1) == 1, "unable to read 1 character from the stream"); 401 peeked = temp[0]; 402 } 403 } 404 405 public String read(int charCount) throws FHIRException { 406 String value = ""; 407 for (int i = 0; i < charCount; i++) 408 value = value + read(); 409 return value; 410 } 411 412 public void skipEOL() throws FHIRException { 413 while (!finished && (peek() == '\r' || peek() == '\n')) 414 read(); 415 } 416 417 public char read() throws FHIRException { 418 rule(!finished, "No more content to read"); 419 char value = peek(); 420 offset++; 421 if (value == '\r' || value == '\n') { 422 if (lastValue != '\r' || value != '\n') 423 lineNumber++; 424 } 425 lastValue = value; 426 try { 427 next(); 428 } catch (Exception e) { 429 throw new FHIRException(e); 430 } 431 return value; 432 } 433 434 public boolean isFinished() { 435 return finished; 436 } 437 438 public char peek() throws FHIRException { 439 rule(!finished, "Cannot peek"); 440 return peeked; 441 } 442 443 public void mark() { 444 stream.mark(2048); 445 } 446 447 public void reset() throws FHIRException { 448 try { 449 stream.reset(); 450 } catch (IOException e) { 451 throw new FHIRException(e); 452 } 453 open(); 454 } 455 456 public boolean IsEOL() throws FHIRException { 457 return peek() == '\r' || peek() == '\n'; 458 } 459 460 public int getLineNumber() { 461 return lineNumber; 462 } 463 464 public int getOffset() { 465 return offset; 466 } 467 468 } 469 470 public VerticalBarParser(IWorkerContext context) { 471 super(context); 472 } 473 474 private String charset = "ASCII"; 475 private Delimiters delimiters = new Delimiters(); 476 477 @Override 478 public Element parse(InputStream stream) throws IOException, FHIRFormatError, DefinitionException, FHIRException { 479 StructureDefinition sd = context.fetchResource(StructureDefinition.class, 480 "http://hl7.org/fhir/v2/StructureDefinition/Message"); 481 Element message = new Element("Message", new Property(context, sd.getSnapshot().getElementFirstRep(), sd)); 482 VerticalBarParserReader reader = new VerticalBarParserReader(new BufferedInputStream(stream), charset); 483 484 preDecode(reader); 485 while (!reader.isFinished()) // && (getOptions().getSegmentLimit() == 0 || getOptions().getSegmentLimit() > 486 // message.getSegments().size())) 487 readSegment(message, reader); 488 489 return message; 490 } 491 492 private void preDecode(VerticalBarParserReader reader) throws FHIRException { 493 reader.skipEOL(); 494 String temp = reader.read(3); 495 rule(temp.equals("MSH") || temp.equals("FHS"), "Found '" + temp + "' looking for 'MSH' or 'FHS'"); 496 readDelimiters(reader); 497 // readVersion(message); - probably don't need to do that? 498 // readCharacterSet(); 499 reader.reset(); // ready to read message now 500 } 501 502 private void rule(boolean test, String msg) throws FHIRException { 503 if (!test) 504 throw new FHIRException(msg); 505 } 506 507 private void readDelimiters(VerticalBarParserReader reader) throws FHIRException { 508 delimiters.setFieldDelimiter(reader.read()); 509 if (!(reader.peek() == delimiters.getFieldDelimiter())) 510 delimiters.setComponentDelimiter(reader.read()); 511 if (!(reader.peek() == delimiters.getFieldDelimiter())) 512 delimiters.setRepetitionDelimiter(reader.read()); 513 if (!(reader.peek() == delimiters.getFieldDelimiter())) 514 delimiters.setEscapeCharacter(reader.read()); 515 if (!(reader.peek() == delimiters.getFieldDelimiter())) 516 delimiters.setSubComponentDelimiter(reader.read()); 517 delimiters.check(); 518 } 519 520 private void readSegment(Element message, VerticalBarParserReader reader) throws FHIRException { 521 Element segment = new Element("segment", message.getProperty().getChild("segment")); 522 message.getChildren().add(segment); 523 Element segmentCode = new Element("code", segment.getProperty().getChild("code")); 524 segment.getChildren().add(segmentCode); 525 segmentCode.setValue(reader.read(3)); 526 527 int index = 0; 528 while (!reader.isFinished() && !reader.IsEOL()) { 529 index++; 530 readField(reader, segment, index); 531 if (!reader.isFinished() && !reader.IsEOL()) 532 rule(reader.read() == delimiters.getFieldDelimiter(), "Expected to find field delimiter"); 533 } 534 if (!reader.isFinished()) 535 reader.skipEOL(); 536 } 537 538 private void readField(VerticalBarParserReader reader, Element segment, int index) { 539 // TODO Auto-generated method stub 540 541 } 542 543 @Override 544 public void compose(Element e, OutputStream destination, OutputStyle style, String base) { 545 // TODO Auto-generated method stub 546 547 } 548 549}