http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/FlowFileUnpackagerV3.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/FlowFileUnpackagerV3.java b/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/FlowFileUnpackagerV3.java new file mode 100644 index 0000000..f937585 --- /dev/null +++ b/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/FlowFileUnpackagerV3.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.util; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class FlowFileUnpackagerV3 implements FlowFileUnpackager { + + private byte[] nextHeader = null; + private boolean haveReadSomething = false; + private final byte readBuffer[] = new byte[8192]; + + @Override + public boolean hasMoreData() throws IOException { + return nextHeader != null || !haveReadSomething; + } + + private byte[] readHeader(final InputStream in) throws IOException { + final byte[] header = new byte[FlowFilePackagerV3.MAGIC_HEADER.length]; + for (int i = 0; i < header.length; i++) { + final int next = in.read(); + if (next < 0) { + if (i == 0) { + return null; + } + + throw new IOException("Not in FlowFile-v3 format"); + } + header[i] = (byte) (next & 0xFF); + } + + return header; + } + + @Override + public Map<String, String> unpackageFlowFile(final InputStream in, final OutputStream out) throws IOException { + final byte[] header = (nextHeader == null) ? readHeader(in) : nextHeader; + if (!Arrays.equals(header, FlowFilePackagerV3.MAGIC_HEADER)) { + throw new IOException("Not in FlowFile-v3 format"); + } + + final Map<String, String> attributes = readAttributes(in); + final long expectedNumBytes = readLong(in); // read length of payload + copy(in, out, expectedNumBytes); // read payload + + nextHeader = readHeader(in); + haveReadSomething = true; + + return attributes; + } + + protected Map<String, String> readAttributes(final InputStream in) throws IOException { + final Map<String, String> attributes = new HashMap<>(); + final Integer numAttributes = readFieldLength(in); //read number of attributes + if (numAttributes == null) { + return null; + } + if (numAttributes == 0) { + throw new IOException("flow files cannot have zero attributes"); + } + for (int i = 0; i < numAttributes; i++) { //read each attribute key/value pair + final String key = readString(in); + final String value = readString(in); + attributes.put(key, value); + } + + return attributes; + } + + protected String readString(final InputStream in) throws IOException { + final Integer numBytes = readFieldLength(in); + if (numBytes == null) { + throw new EOFException(); + } + final byte[] bytes = new byte[numBytes]; + fillBuffer(in, bytes, numBytes); + return new String(bytes, "UTF-8"); + } + + private void fillBuffer(final InputStream in, final byte[] buffer, final int length) throws IOException { + int bytesRead; + int totalBytesRead = 0; + while ((bytesRead = in.read(buffer, totalBytesRead, length - totalBytesRead)) > 0) { + totalBytesRead += bytesRead; + } + if (totalBytesRead != length) { + throw new EOFException(); + } + } + + protected long copy(final InputStream in, final OutputStream out, final long numBytes) throws IOException { + int bytesRead; + long totalBytesRead = 0L; + while ((bytesRead = in.read(readBuffer, 0, (int) Math.min(readBuffer.length, numBytes - totalBytesRead))) > 0) { + out.write(readBuffer, 0, bytesRead); + totalBytesRead += bytesRead; + } + + if (totalBytesRead < numBytes) { + throw new EOFException("Expected " + numBytes + " but received " + totalBytesRead); + } + + return totalBytesRead; + } + + protected long readLong(final InputStream in) throws IOException { + fillBuffer(in, readBuffer, 8); + return (((long) readBuffer[0] << 56) + + ((long) (readBuffer[1] & 255) << 48) + + ((long) (readBuffer[2] & 255) << 40) + + ((long) (readBuffer[3] & 255) << 32) + + ((long) (readBuffer[4] & 255) << 24) + + ((readBuffer[5] & 255) << 16) + + ((readBuffer[6] & 255) << 8) + + ((readBuffer[7] & 255))); + } + + private Integer readFieldLength(final InputStream in) throws IOException { + final int firstValue = in.read(); + final int secondValue = in.read(); + if (firstValue < 0) { + return null; + } + if (secondValue < 0) { + throw new EOFException(); + } + if (firstValue == 0xff && secondValue == 0xff) { + int ch1 = in.read(); + int ch2 = in.read(); + int ch3 = in.read(); + int ch4 = in.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) { + throw new EOFException(); + } + return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4)); + } else { + return ((firstValue << 8) + (secondValue)); + } + } + +}
http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/Unpackage.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/Unpackage.java b/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/Unpackage.java new file mode 100644 index 0000000..19f702c --- /dev/null +++ b/nifi-commons/nifi-flowfile-packager/src/main/java/org/apache/nifi/util/Unpackage.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.UUID; + +public class Unpackage { + + private static void printUsage() { + System.out.println("java " + Unpackage.class.getCanonicalName() + " <version> <input file 1> [<input file 2> <input file 3> ... <input file N>]"); + System.out.println("<version> : The version of the FlowFile Package format. Valid values are 1, 2, 3"); + System.out.println("<input file X> : The FlowFile package to unpack"); + System.out.println(); + } + + public static void main(final String[] args) throws IOException { + if (args.length < 2) { + printUsage(); + return; + } + + final String version = args[0]; + + int inputFileCount = 0; + int outputFileCount = 0; + + for (int i = 1; i < args.length; i++) { + final String filename = args[i]; + final File inFile = new File(filename); + + if (inFile.isDirectory()) { + System.out.println("WARNING: input file " + inFile + " is a directory; skipping"); + continue; + } + + if (!inFile.exists() || !inFile.canRead()) { + System.out.println("ERROR: unable to read file " + inFile); + continue; + } + + final File outputDir = new File(inFile.getAbsolutePath() + ".unpacked"); + if (!outputDir.exists() && !outputDir.mkdir()) { + System.out.println("ERROR: Unable to create directory " + outputDir); + continue; + } + + final File tempFile = new File(outputDir, ".temp." + UUID.randomUUID().toString() + ".unpackage"); + inputFileCount++; + try (final FileInputStream fis = new FileInputStream(inFile); + final BufferedInputStream bufferedIn = new BufferedInputStream(fis)) { + + final FlowFileUnpackager unpackager = createUnpackager(version); + while (unpackager.hasMoreData()) { + outputFileCount++; + final Map<String, String> attributes; + + try (final FileOutputStream fos = new FileOutputStream(tempFile); + final BufferedOutputStream bufferedOut = new BufferedOutputStream(fos)) { + attributes = unpackager.unpackageFlowFile(bufferedIn, bufferedOut); + } + + String outputFilename = attributes.get("filename"); + if (outputFilename == null) { + outputFilename = attributes.get("nf.file.name"); + } + + final File outputFile = new File(outputDir, outputFilename); + tempFile.renameTo(outputFile); + + final File attributeFilename = new File(outputDir, outputFilename + ".attributes"); + try (final FileOutputStream fos = new FileOutputStream(attributeFilename); + final BufferedOutputStream bufferedOut = new BufferedOutputStream(fos)) { + + for (final Map.Entry<String, String> entry : attributes.entrySet()) { + bufferedOut.write((entry.getKey() + "=" + entry.getValue() + "\n").getBytes("UTF-8")); + } + } + } + } + } + + System.out.println("Unpacked " + inputFileCount + " packages into " + outputFileCount + " files"); + } + + public static FlowFileUnpackager createUnpackager(final String version) { + switch (version) { + case "1": + return new FlowFileUnpackagerV1(); + case "2": + return new FlowFileUnpackagerV2(); + case "3": + return new FlowFileUnpackagerV3(); + default: + System.out.println("ERROR: Invalid version: " + version + "; must be 1, 2, or 3"); + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-flowfile-packager/src/test/java/org/apache/nifi/util/TestPackageUnpackageV3.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-flowfile-packager/src/test/java/org/apache/nifi/util/TestPackageUnpackageV3.java b/nifi-commons/nifi-flowfile-packager/src/test/java/org/apache/nifi/util/TestPackageUnpackageV3.java new file mode 100644 index 0000000..24cd374 --- /dev/null +++ b/nifi-commons/nifi-flowfile-packager/src/test/java/org/apache/nifi/util/TestPackageUnpackageV3.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +public class TestPackageUnpackageV3 { + + @Test + public void test() throws IOException { + final FlowFilePackager packager = new FlowFilePackagerV3(); + final FlowFileUnpackager unpackager = new FlowFileUnpackagerV3(); + + final byte[] data = "Hello, World!".getBytes("UTF-8"); + final Map<String, String> map = new HashMap<>(); + map.put("abc", "cba"); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ByteArrayInputStream in = new ByteArrayInputStream(data); + packager.packageFlowFile(in, baos, map, data.length); + + final byte[] encoded = baos.toByteArray(); + final ByteArrayInputStream encodedIn = new ByteArrayInputStream(encoded); + final ByteArrayOutputStream decodedOut = new ByteArrayOutputStream(); + final Map<String, String> unpackagedAttributes = unpackager.unpackageFlowFile(encodedIn, decodedOut); + final byte[] decoded = decodedOut.toByteArray(); + + assertEquals(map, unpackagedAttributes); + assertTrue(Arrays.equals(data, decoded)); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/.gitignore ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/.gitignore b/nifi-commons/nifi-hl7-query-language/.gitignore new file mode 100644 index 0000000..e91d5c4 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/.gitignore @@ -0,0 +1,3 @@ +/target/ +/target/ +/target/ http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/pom.xml b/nifi-commons/nifi-hl7-query-language/pom.xml new file mode 100644 index 0000000..8b04e5c --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/pom.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-commons</artifactId> + <version>0.3.0-SNAPSHOT</version> + </parent> + + <artifactId>nifi-hl7-query-language</artifactId> + <packaging>jar</packaging> + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.7</source> + <target>1.7</target> + </configuration> + </plugin> + <plugin> + <groupId>org.antlr</groupId> + <artifactId>antlr3-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>antlr</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes combine.children="append"> + <exclude>src/test/resources/hypoglycemia</exclude> + <exclude>src/test/resources/hyperglycemia</exclude> + </excludes> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + <configuration> + <excludes>**/HL7QueryParser.java,**/HL7QueryLexer.java</excludes> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.antlr</groupId> + <artifactId>antlr-runtime</artifactId> + </dependency> + + <!-- HAPI to parse v2 messages --> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-base</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v21</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v22</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v23</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v231</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v24</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v25</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v251</artifactId> + <version>2.2</version> + </dependency> + <dependency> + <groupId>ca.uhn.hapi</groupId> + <artifactId>hapi-structures-v26</artifactId> + <version>2.2</version> + </dependency> + + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g b/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g new file mode 100644 index 0000000..478028b --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +lexer grammar HL7QueryLexer; + +@header { + package org.apache.nifi.hl7.query.antlr; + import org.apache.nifi.hl7.query.exception.HL7QueryParsingException; +} + +@rulecatch { + catch(final Exception e) { + throw new HL7QueryParsingException(e); + } +} + +@members { + public void displayRecognitionError(String[] tokenNames, RecognitionException e) { + final StringBuilder sb = new StringBuilder(); + if ( e.token == null ) { + sb.append("Unrecognized token "); + } else { + sb.append("Unexpected token '").append(e.token.getText()).append("' "); + } + sb.append("at line ").append(e.line); + if ( e.approximateLineInfo ) { + sb.append(" (approximately)"); + } + sb.append(", column ").append(e.charPositionInLine); + sb.append(". Query: ").append(e.input.toString()); + + throw new HL7QueryParsingException(sb.toString()); + } + + public void recover(RecognitionException e) { + final StringBuilder sb = new StringBuilder(); + if ( e.token == null ) { + sb.append("Unrecognized token "); + } else { + sb.append("Unexpected token '").append(e.token.getText()).append("' "); + } + sb.append("at line ").append(e.line); + if ( e.approximateLineInfo ) { + sb.append(" (approximately)"); + } + sb.append(", column ").append(e.charPositionInLine); + sb.append(". Query: ").append(e.input.toString()); + + throw new HL7QueryParsingException(sb.toString()); + } +} + + +// PUNCTUATION & SPECIAL CHARACTERS +WHITESPACE : (' '|'\t'|'\n'|'\r')+ { $channel = HIDDEN; }; +COMMENT : '#' ( ~('\n') )* '\n' { $channel = HIDDEN; }; + +LPAREN : '('; +RPAREN : ')'; +LBRACE : '{'; +RBRACE : '}'; +COLON : ':'; +COMMA : ','; +DOT : '.'; +SEMICOLON : ';'; + + + +// OPERATORS +EQUALS : '='; +NOT_EQUALS : '!='; +GT : '>'; +GE : '>='; +LT : '<'; +LE : '<='; +REGEX : 'MATCHES REGEX'; +LIKE : 'LIKE'; +IS_NULL : 'IS NULL'; +NOT_NULL : 'NOT NULL'; + + +// KEYWORDS +AND : 'AND'; +OR : 'OR'; +NOT : 'NOT'; + +TRUE : 'true'; +FALSE : 'false'; + +SELECT : 'select' | 'SELECT'; +DECLARE : 'declare' | 'DECLARE'; +OPTIONAL : 'optional' | 'OPTIONAL'; +REQUIRED : 'required' | 'REQUIRED'; +AS : 'as' | 'AS'; +WHERE : 'where' | 'WHERE'; + +MESSAGE : 'MESSAGE' | 'message'; +SEGMENT : 'SEGMENT' | 'segment'; + + +SEGMENT_NAME : LETTER ALPHA_NUMERIC ALPHA_NUMERIC; + + +NUMBER : ('0'..'9')+; +fragment LETTER : 'A'..'Z'; +fragment ALPHA_NUMERIC : 'A'..'Z' | '0'..'9'; + + +// STRINGS +STRING_LITERAL +@init{StringBuilder lBuf = new StringBuilder();} + : + ( + '"' + ( + escaped=ESC {lBuf.append(getText());} | + normal = ~( '"' | '\\' | '\n' | '\r' | '\t' ) { lBuf.appendCodePoint(normal);} + )* + '"' + ) + { + setText(lBuf.toString()); + } + | + ( + '\'' + ( + escaped=ESC {lBuf.append(getText());} | + normal = ~( '\'' | '\\' | '\n' | '\r' | '\t' ) { lBuf.appendCodePoint(normal);} + )* + '\'' + ) + { + setText(lBuf.toString()); + } + ; + + +fragment +ESC + : '\\' + ( + '"' { setText("\""); } + | '\'' { setText("\'"); } + | 'r' { setText("\r"); } + | 'n' { setText("\n"); } + | 't' { setText("\t"); } + | '\\' { setText("\\\\"); } + | nextChar = ~('"' | '\'' | 'r' | 'n' | 't' | '\\') + { + StringBuilder lBuf = new StringBuilder(); lBuf.append("\\\\").appendCodePoint(nextChar); setText(lBuf.toString()); + } + ) + ; + +IDENTIFIER : ( + ~('$' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ':' | ';' | '/' | '*' | '\'' | ' ' | '\t' | '\r' | '\n' | '0'..'9' | '.') + ~('$' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ':' | ';' | '/' | '*' | '\'' | ' ' | '\t' | '\r' | '\n' | '.')* + ); http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g b/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g new file mode 100644 index 0000000..f051872 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parser grammar HL7QueryParser; + +options { + output=AST; + tokenVocab=HL7QueryLexer; +} + +tokens { + QUERY; + DECLARATION; +} + +@header { + package org.apache.nifi.hl7.query.antlr; + import org.apache.nifi.hl7.query.exception.HL7QueryParsingException; +} + +@members { + public void displayRecognitionError(String[] tokenNames, RecognitionException e) { + final StringBuilder sb = new StringBuilder(); + if ( e.token == null ) { + sb.append("Unrecognized token "); + } else { + sb.append("Unexpected token '").append(e.token.getText()).append("' "); + } + sb.append("at line ").append(e.line); + if ( e.approximateLineInfo ) { + sb.append(" (approximately)"); + } + sb.append(", column ").append(e.charPositionInLine); + sb.append(". Query: ").append(e.input.toString()); + + throw new HL7QueryParsingException(sb.toString()); + } + + public void recover(final RecognitionException e) { + final StringBuilder sb = new StringBuilder(); + if ( e.token == null ) { + sb.append("Unrecognized token "); + } else { + sb.append("Unexpected token '").append(e.token.getText()).append("' "); + } + sb.append("at line ").append(e.line); + if ( e.approximateLineInfo ) { + sb.append(" (approximately)"); + } + sb.append(", column ").append(e.charPositionInLine); + sb.append(". Query: ").append(e.input.toString()); + + throw new HL7QueryParsingException(sb.toString()); + } +} + + +declareClause : DECLARE^ declaration (COMMA! declaration)*; + +requiredOrOptional : REQUIRED | OPTIONAL; +declaration : IDENTIFIER AS requiredOrOptional SEGMENT_NAME -> + ^(DECLARATION IDENTIFIER requiredOrOptional SEGMENT_NAME); + + +selectClause : SELECT^ selectableClause; +selectableClause : selectable (COMMA! selectable)*; +selectable : (MESSAGE | ref | field)^ (AS! IDENTIFIER^)?; + + +whereClause : WHERE^ conditions; + +conditions : condition ((AND^ | OR^) condition)*; + +condition : NOT^ condition | LPAREN! conditions RPAREN! | evaluation; + +evaluation : expression + ( + unaryOperator^ + | (binaryOperator^ expression) + ); + +expression : (LPAREN! expr RPAREN!) | expr; +expr : ref | field | STRING_LITERAL | NUMBER; + +unaryOperator : IS_NULL | NOT_NULL; +binaryOperator : EQUALS | NOT_EQUALS | LT | GT | LE | GE; + +ref : (SEGMENT_NAME | IDENTIFIER); +field : ref DOT^ NUMBER + (DOT^ NUMBER (DOT^ NUMBER (DOT^ NUMBER)?)?)?; + + +query : declareClause? selectClause whereClause? EOF -> + ^(QUERY declareClause? selectClause whereClause?); http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java new file mode 100644 index 0000000..dc73efb --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.hapi; + +import java.util.Collections; +import java.util.List; + +import org.apache.nifi.hl7.model.HL7Component; +import org.apache.nifi.hl7.model.HL7Field; + +public class EmptyField implements HL7Field { + + @Override + public String getValue() { + return null; + } + + @Override + public List<HL7Component> getComponents() { + return Collections.emptyList(); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java new file mode 100644 index 0000000..94cce5c --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.hapi; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.nifi.hl7.model.HL7Component; +import org.apache.nifi.hl7.model.HL7Field; + +import ca.uhn.hl7v2.model.Composite; +import ca.uhn.hl7v2.model.ExtraComponents; +import ca.uhn.hl7v2.model.Primitive; +import ca.uhn.hl7v2.model.Type; +import ca.uhn.hl7v2.model.Varies; +import ca.uhn.hl7v2.parser.EncodingCharacters; +import ca.uhn.hl7v2.parser.PipeParser; + +public class HapiField implements HL7Field, HL7Component { + + private final String value; + private final List<HL7Component> components; + + public HapiField(final Type type) { + this.value = PipeParser.encode(type, EncodingCharacters.defaultInstance()); + + final List<HL7Component> componentList = new ArrayList<>(); + if (type instanceof Composite) { + final Composite composite = (Composite) type; + + for (final Type component : composite.getComponents()) { + componentList.add(new HapiField(component)); + } + } + + final ExtraComponents extra = type.getExtraComponents(); + if (extra != null && extra.numComponents() > 0) { + final String singleFieldValue; + if (type instanceof Primitive) { + singleFieldValue = ((Primitive) type).getValue(); + } else { + singleFieldValue = this.value; + } + componentList.add(new SingleValueField(singleFieldValue)); + + for (int i = 0; i < extra.numComponents(); i++) { + final Varies varies = extra.getComponent(i); + componentList.add(new HapiField(varies)); + } + } + + this.components = Collections.unmodifiableList(componentList); + } + + @Override + public String getValue() { + return value; + } + + @Override + public List<HL7Component> getComponents() { + return components; + } + + @Override + public String toString() { + return value; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java new file mode 100644 index 0000000..74a8f6c --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.hapi; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.nifi.hl7.model.HL7Message; +import org.apache.nifi.hl7.model.HL7Segment; + +import ca.uhn.hl7v2.HL7Exception; +import ca.uhn.hl7v2.model.Group; +import ca.uhn.hl7v2.model.Message; +import ca.uhn.hl7v2.model.Segment; +import ca.uhn.hl7v2.model.Structure; + +public class HapiMessage implements HL7Message { + + private final Message message; + private final List<HL7Segment> allSegments; + private final Map<String, List<HL7Segment>> segmentMap; + + public HapiMessage(final Message message) throws HL7Exception { + this.message = message; + + allSegments = new ArrayList<>(); + populateSegments(message, allSegments); + + segmentMap = new HashMap<>(); + for (final HL7Segment segment : allSegments) { + final String segmentName = segment.getName(); + List<HL7Segment> segmentList = segmentMap.get(segmentName); + if (segmentList == null) { + segmentList = new ArrayList<>(); + segmentMap.put(segmentName, segmentList); + } + + segmentList.add(segment); + } + } + + private void populateSegments(final Group group, final List<HL7Segment> segments) throws HL7Exception { + for (final String structureName : group.getNames()) { + final Structure[] structures = group.getAll(structureName); + if (group.isGroup(structureName)) { + for (final Structure structure : structures) { + populateSegments((Group) structure, segments); + } + } else { + for (final Structure structure : structures) { + final Segment segment = (Segment) structure; + final HapiSegment hapiSegment = new HapiSegment(segment); + segments.add(hapiSegment); + } + } + } + } + + @Override + public List<HL7Segment> getSegments() { + return Collections.unmodifiableList(allSegments); + } + + @Override + public List<HL7Segment> getSegments(final String segmentType) { + final List<HL7Segment> segments = segmentMap.get(segmentType); + if (segments == null) { + return Collections.emptyList(); + } + + return Collections.unmodifiableList(segments); + } + + @Override + public String toString() { + return message.toString(); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java new file mode 100644 index 0000000..20e646c --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.hapi; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.nifi.hl7.model.HL7Field; +import org.apache.nifi.hl7.model.HL7Segment; + +import ca.uhn.hl7v2.HL7Exception; +import ca.uhn.hl7v2.model.Segment; +import ca.uhn.hl7v2.model.Type; + +public class HapiSegment implements HL7Segment { + + private final Segment segment; + private final List<HL7Field> fields; + + public HapiSegment(final Segment segment) throws HL7Exception { + this.segment = segment; + + final List<HL7Field> fieldList = new ArrayList<>(); + for (int i = 1; i <= segment.numFields(); i++) { + final Type[] types = segment.getField(i); + + if (types == null || types.length == 0) { + fieldList.add(new EmptyField()); + continue; + } + + for (final Type type : types) { + fieldList.add(new HapiField(type)); + } + } + + this.fields = Collections.unmodifiableList(fieldList); + } + + @Override + public String getName() { + return segment.getName(); + } + + @Override + public List<HL7Field> getFields() { + return fields; + } + + @Override + public String toString() { + return segment.toString(); + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java new file mode 100644 index 0000000..b8a6351 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.hapi; + +import java.util.Collections; +import java.util.List; + +import org.apache.nifi.hl7.model.HL7Component; +import org.apache.nifi.hl7.model.HL7Field; + +public class SingleValueField implements HL7Field { + + private final String value; + + public SingleValueField(final String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + @Override + public List<HL7Component> getComponents() { + return Collections.emptyList(); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java new file mode 100644 index 0000000..4d2f276 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.io; + +import java.io.IOException; + +import org.apache.nifi.hl7.model.HL7Message; + +public interface HL7Reader { + + HL7Message nextMessage() throws IOException; + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java new file mode 100644 index 0000000..c0c74f9 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.io.exception; + +import java.io.IOException; + +public class InvalidHL7Exception extends IOException { + + private static final long serialVersionUID = -5675416667224562441L; + + public InvalidHL7Exception() { + super(); + } + + public InvalidHL7Exception(String message, Throwable cause) { + super(message, cause); + } + + public InvalidHL7Exception(String message) { + super(message); + } + + public InvalidHL7Exception(Throwable cause) { + super(cause); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java new file mode 100644 index 0000000..d8c1495 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.model; + +import java.util.List; + +public interface HL7Component { + + String getValue(); + + List<HL7Component> getComponents(); +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java new file mode 100644 index 0000000..ff58d96 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.model; + +public interface HL7Field extends HL7Component { +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java new file mode 100644 index 0000000..dc68c2b --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.model; + +import java.util.List; + +public interface HL7Message { + + List<HL7Segment> getSegments(); + + List<HL7Segment> getSegments(String segmentType); + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java new file mode 100644 index 0000000..d5d67ae --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.model; + +import java.util.List; + +public interface HL7Segment { + + String getName(); + + List<HL7Field> getFields(); + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java new file mode 100644 index 0000000..94998e7 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query; + +import org.apache.nifi.hl7.model.HL7Message; + +public interface Declaration { + + String getAlias(); + + boolean isRequired(); + + Object getDeclaredValue(HL7Message message); + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java new file mode 100644 index 0000000..3734b4f --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java @@ -0,0 +1,384 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.tree.Tree; +import org.apache.nifi.hl7.model.HL7Message; +import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator; +import org.apache.nifi.hl7.query.evaluator.Evaluator; +import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.EqualsEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.GreaterThanEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.GreaterThanOrEqualEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.IsNullEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.LessThanEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.LessThanOrEqualEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.NotEqualsEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.NotEvaluator; +import org.apache.nifi.hl7.query.evaluator.comparison.NotNullEvaluator; +import org.apache.nifi.hl7.query.evaluator.literal.IntegerLiteralEvaluator; +import org.apache.nifi.hl7.query.evaluator.literal.StringLiteralEvaluator; +import org.apache.nifi.hl7.query.evaluator.logic.AndEvaluator; +import org.apache.nifi.hl7.query.evaluator.logic.OrEvaluator; +import org.apache.nifi.hl7.query.evaluator.message.DeclaredReferenceEvaluator; +import org.apache.nifi.hl7.query.evaluator.message.DotEvaluator; +import org.apache.nifi.hl7.query.evaluator.message.MessageEvaluator; +import org.apache.nifi.hl7.query.evaluator.message.SegmentEvaluator; +import org.apache.nifi.hl7.query.exception.HL7QueryParsingException; +import org.apache.nifi.hl7.query.result.MissedResult; +import org.apache.nifi.hl7.query.result.StandardQueryResult; + +import org.apache.nifi.hl7.query.antlr.HL7QueryLexer; +import org.apache.nifi.hl7.query.antlr.HL7QueryParser; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.AND; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.DECLARE; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.DOT; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.EQUALS; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.GE; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.GT; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.IDENTIFIER; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.IS_NULL; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.LE; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.LT; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.MESSAGE; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.NOT; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.NOT_EQUALS; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.NOT_NULL; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.NUMBER; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.OR; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.REQUIRED; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.SEGMENT_NAME; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.SELECT; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.STRING_LITERAL; +import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.WHERE; + +public class HL7Query { + + private final Tree tree; + private final String query; + private final Set<Declaration> declarations = new HashSet<>(); + + private final List<Selection> selections; + private final BooleanEvaluator whereEvaluator; + + private HL7Query(final Tree tree, final String query) { + this.tree = tree; + this.query = query; + + List<Selection> select = null; + BooleanEvaluator where = null; + for (int i = 0; i < tree.getChildCount(); i++) { + final Tree child = tree.getChild(i); + + switch (child.getType()) { + case DECLARE: + processDeclare(child); + break; + case SELECT: + select = processSelect(child); + break; + case WHERE: + where = processWhere(child); + break; + default: + throw new HL7QueryParsingException("Found unexpected clause at root level: " + tree.getText()); + } + } + + this.whereEvaluator = where; + this.selections = select; + } + + private void processDeclare(final Tree declare) { + for (int i = 0; i < declare.getChildCount(); i++) { + final Tree declarationTree = declare.getChild(i); + + final String identifier = declarationTree.getChild(0).getText(); + final Tree requiredOrOptionalTree = declarationTree.getChild(1); + final boolean required = requiredOrOptionalTree.getType() == REQUIRED; + + final String segmentName = declarationTree.getChild(2).getText(); + + final Declaration declaration = new Declaration() { + @Override + public String getAlias() { + return identifier; + } + + @Override + public boolean isRequired() { + return required; + } + + @Override + public Object getDeclaredValue(final HL7Message message) { + if (message == null) { + return null; + } + + return message.getSegments(segmentName); + } + }; + + declarations.add(declaration); + } + } + + private List<Selection> processSelect(final Tree select) { + final List<Selection> selections = new ArrayList<>(); + + for (int i = 0; i < select.getChildCount(); i++) { + final Tree selectable = select.getChild(i); + + final String alias = getSelectedName(selectable); + final Evaluator<?> selectionEvaluator = buildReferenceEvaluator(selectable); + final Selection selection = new Selection(selectionEvaluator, alias); + selections.add(selection); + } + + return selections; + } + + private String getSelectedName(final Tree selectable) { + if (selectable.getChildCount() == 0) { + return selectable.getText(); + } else if (selectable.getType() == DOT) { + return getSelectedName(selectable.getChild(0)) + "." + getSelectedName(selectable.getChild(1)); + } else { + return selectable.getChild(selectable.getChildCount() - 1).getText(); + } + } + + private BooleanEvaluator processWhere(final Tree where) { + return buildBooleanEvaluator(where.getChild(0)); + } + + private Evaluator<?> buildReferenceEvaluator(final Tree tree) { + switch (tree.getType()) { + case MESSAGE: + return new MessageEvaluator(); + case SEGMENT_NAME: + return new SegmentEvaluator(new StringLiteralEvaluator(tree.getText())); + case IDENTIFIER: + return new DeclaredReferenceEvaluator(new StringLiteralEvaluator(tree.getText())); + case DOT: + final Tree firstChild = tree.getChild(0); + final Tree secondChild = tree.getChild(1); + return new DotEvaluator(buildReferenceEvaluator(firstChild), buildIntegerEvaluator(secondChild)); + case STRING_LITERAL: + return new StringLiteralEvaluator(tree.getText()); + case NUMBER: + return new IntegerLiteralEvaluator(Integer.parseInt(tree.getText())); + default: + throw new HL7QueryParsingException("Failed to build evaluator for " + tree.getText()); + } + } + + private IntegerEvaluator buildIntegerEvaluator(final Tree tree) { + switch (tree.getType()) { + case NUMBER: + return new IntegerLiteralEvaluator(Integer.parseInt(tree.getText())); + default: + throw new HL7QueryParsingException("Failed to build Integer Evaluator for " + tree.getText()); + } + } + + private BooleanEvaluator buildBooleanEvaluator(final Tree tree) { + // TODO: add Date comparisons + // LT/GT/GE/GE should allow for dates based on Field's Type + // BETWEEN + // DATE('2015/01/01') + // DATE('2015/01/01 12:00:00') + // DATE('24 HOURS AGO') + // DATE('YESTERDAY') + + switch (tree.getType()) { + case EQUALS: + return new EqualsEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1))); + case NOT_EQUALS: + return new NotEqualsEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1))); + case GT: + return new GreaterThanEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1))); + case LT: + return new LessThanEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1))); + case GE: + return new GreaterThanOrEqualEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1))); + case LE: + return new LessThanOrEqualEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1))); + case NOT: + return new NotEvaluator(buildBooleanEvaluator(tree.getChild(0))); + case AND: + return new AndEvaluator(buildBooleanEvaluator(tree.getChild(0)), buildBooleanEvaluator(tree.getChild(1))); + case OR: + return new OrEvaluator(buildBooleanEvaluator(tree.getChild(0)), buildBooleanEvaluator(tree.getChild(1))); + case IS_NULL: + return new IsNullEvaluator(buildReferenceEvaluator(tree.getChild(0))); + case NOT_NULL: + return new NotNullEvaluator(buildReferenceEvaluator(tree.getChild(0))); + default: + throw new HL7QueryParsingException("Cannot build boolean evaluator for '" + tree.getText() + "'"); + } + } + + Tree getTree() { + return tree; + } + + public String getQuery() { + return query; + } + + @Override + public String toString() { + return "HL7Query[" + query + "]"; + } + + public static HL7Query compile(final String query) { + try { + final CommonTokenStream lexerTokenStream = createTokenStream(query); + final HL7QueryParser parser = new HL7QueryParser(lexerTokenStream); + final Tree tree = (Tree) parser.query().getTree(); + + return new HL7Query(tree, query); + } catch (final HL7QueryParsingException e) { + throw e; + } catch (final Exception e) { + throw new HL7QueryParsingException(e); + } + } + + private static CommonTokenStream createTokenStream(final String expression) throws HL7QueryParsingException { + final CharStream input = new ANTLRStringStream(expression); + final HL7QueryLexer lexer = new HL7QueryLexer(input); + return new CommonTokenStream(lexer); + } + + public List<Class<?>> getReturnTypes() { + final List<Class<?>> returnTypes = new ArrayList<>(); + + for (final Selection selection : selections) { + returnTypes.add(selection.getEvaluator().getType()); + } + + return returnTypes; + } + + @SuppressWarnings("unchecked") + public QueryResult evaluate(final HL7Message message) { + + int totalIterations = 1; + final LinkedHashMap<String, List<Object>> possibleValueMap = new LinkedHashMap<>(); + for (final Declaration declaration : declarations) { + final Object value = declaration.getDeclaredValue(message); + if (value == null && declaration.isRequired()) { + return new MissedResult(selections); + } + + final List<Object> possibleValues; + if (value instanceof List) { + possibleValues = (List<Object>) value; + } else if (value instanceof Collection) { + possibleValues = new ArrayList<Object>((Collection<Object>) value); + } else { + possibleValues = new ArrayList<>(1); + possibleValues.add(value); + } + + if (possibleValues.isEmpty()) { + return new MissedResult(selections); + } + + possibleValueMap.put(declaration.getAlias(), possibleValues); + totalIterations *= possibleValues.size(); + } + + final Set<Map<String, Object>> resultSet = new HashSet<>(); + for (int i = 0; i < totalIterations; i++) { + final Map<String, Object> aliasValues = assignAliases(possibleValueMap, i); + + aliasValues.put(Evaluator.MESSAGE_KEY, message); + if (whereEvaluator == null || Boolean.TRUE.equals(whereEvaluator.evaluate(aliasValues))) { + final Map<String, Object> resultMap = new HashMap<>(); + + for (final Selection selection : selections) { + final Object value = selection.getEvaluator().evaluate(aliasValues); + resultMap.put(selection.getName(), value); + } + + resultSet.add(resultMap); + } + } + + return new StandardQueryResult(selections, resultSet); + } + + /** + * assigns one of the possible values to each alias, based on which iteration this is. + * require LinkedHashMap just to be very clear and explicit that the order of the Map MUST be guaranteed + * between multiple invocations of this method. + * package protected for testing visibility + */ + static Map<String, Object> assignAliases(final LinkedHashMap<String, List<Object>> possibleValues, final int iteration) { + final Map<String, Object> aliasMap = new HashMap<>(); + + int divisor = 1; + for (final Map.Entry<String, List<Object>> entry : possibleValues.entrySet()) { + final String alias = entry.getKey(); + final List<Object> validValues = entry.getValue(); + + final int idx = (iteration / divisor) % validValues.size(); + final Object obj = validValues.get(idx); + aliasMap.put(alias, obj); + + divisor *= validValues.size(); + } + + return aliasMap; + } + + public String toTreeString() { + final StringBuilder sb = new StringBuilder(); + toTreeString(tree, sb, 0); + return sb.toString(); + } + + private void toTreeString(final Tree tree, final StringBuilder sb, final int indentLevel) { + final String nodeName = tree.getText(); + for (int i = 0; i < indentLevel; i++) { + sb.append(" "); + } + sb.append(nodeName); + sb.append("\n"); + + for (int i = 0; i < tree.getChildCount(); i++) { + final Tree child = tree.getChild(i); + toTreeString(child, sb, indentLevel + 2); + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java new file mode 100644 index 0000000..033ac0a --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query; + +import java.util.List; + +public interface QueryResult { + + boolean isMatch(); + + List<String> getLabels(); + + int getHitCount(); + + ResultHit nextHit(); +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java new file mode 100644 index 0000000..9eb1d8f --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query; + +import java.util.Map; + +public interface ResultHit { + + Object getValue(String label); + + Map<String, Object> getSelectedValues(); +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java new file mode 100644 index 0000000..7b5b7b3 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query; + +import org.apache.nifi.hl7.query.evaluator.Evaluator; + +public class Selection { + + private final Evaluator<?> evaluator; + private final String name; + + public Selection(final Evaluator<?> evaluator, final String name) { + this.evaluator = evaluator; + this.name = name; + } + + public String getName() { + return name; + } + + public Evaluator<?> getEvaluator() { + return evaluator; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java new file mode 100644 index 0000000..98b077e --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query.evaluator; + +public abstract class BooleanEvaluator implements Evaluator<Boolean> { + + public Class<? extends Boolean> getType() { + return Boolean.class; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java new file mode 100644 index 0000000..2d1ddc7 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query.evaluator; + +import java.util.Map; + +public interface Evaluator<T> { + + public static final String MESSAGE_KEY = "message"; + + T evaluate(Map<String, Object> objectMap); + + Class<? extends T> getType(); +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java new file mode 100644 index 0000000..ed56d80 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query.evaluator; + +public abstract class IntegerEvaluator implements Evaluator<Integer> { + + public Class<? extends Integer> getType() { + return Integer.class; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java new file mode 100644 index 0000000..53ab004 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query.evaluator; + +public abstract class StringEvaluator implements Evaluator<String> { + + public Class<? extends String> getType() { + return String.class; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java ---------------------------------------------------------------------- diff --git a/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java new file mode 100644 index 0000000..45edf64 --- /dev/null +++ b/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.hl7.query.evaluator.comparison; + +import java.util.Collection; +import java.util.Map; + +import org.apache.nifi.hl7.model.HL7Field; +import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator; +import org.apache.nifi.hl7.query.evaluator.Evaluator; + +public abstract class AbstractComparisonEvaluator extends BooleanEvaluator { + + private final Evaluator<?> lhs; + private final Evaluator<?> rhs; + + public AbstractComparisonEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) { + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public final Boolean evaluate(final Map<String, Object> objectMap) { + final Object lhsValue = lhs.evaluate(objectMap); + if (lhsValue == null) { + return false; + } + + final Object rhsValue = rhs.evaluate(objectMap); + if (rhsValue == null) { + return false; + } + + return compareRaw(lhsValue, rhsValue); + } + + private Boolean compareRaw(Object lhsValue, Object rhsValue) { + if (lhsValue == null || rhsValue == null) { + return false; + } + + if (lhsValue instanceof HL7Field) { + lhsValue = ((HL7Field) lhsValue).getValue(); + } + + if (rhsValue instanceof HL7Field) { + rhsValue = ((HL7Field) rhsValue).getValue(); + } + + if (lhsValue == null || rhsValue == null) { + return false; + } + + /** + * both are collections, and compare(lhsValue, rhsValue) is false. + * this would be the case, for instance, if we compared field 1 of one segment to + * a field in another segment, and both fields had components. + */ + if (lhsValue instanceof Collection && rhsValue instanceof Collection) { + return false; + } + + /** + * if one side is a collection but the other is not, check if any element in that + * collection compares to the other element in a way that satisfies the condition. + * this would happen, for instance, if we check Segment1.Field5 = 'X' and field 5 repeats + * with a value "A~B~C~X~Y~Z"; in this case we do want to consider Field 5 = X as true. + */ + if (lhsValue instanceof Collection) { + for (final Object lhsObject : (Collection<?>) lhsValue) { + if (compareRaw(lhsObject, rhsValue)) { + return true; + } + } + + return false; + } + + if (rhsValue instanceof Collection) { + for (final Object rhsObject : (Collection<?>) rhsValue) { + if (compareRaw(rhsObject, lhsValue)) { + return true; + } + } + + return false; + } + + if (lhsValue != null && rhsValue != null && compare(lhsValue, rhsValue)) { + return true; + } + + return false; + } + + protected abstract boolean compare(Object lhs, Object rhs); +}