You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by sg...@apache.org on 2021/10/17 21:24:37 UTC

[freemarker-generator] branch FREEMARKER-199 created (now 2f8c40f)

This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a change to branch FREEMARKER-199
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git.


      at 2f8c40f  FREEMARKER-199 [freemarker-generator] Add "utah-parser-tool"

This branch includes the following new commits:

     new 2f8c40f  FREEMARKER-199 [freemarker-generator] Add "utah-parser-tool"

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[freemarker-generator] 01/01: FREEMARKER-199 [freemarker-generator] Add "utah-parser-tool"

Posted by sg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch FREEMARKER-199
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git

commit 2f8c40f772b6384e8f3c0dfa5a16baa49f3489d8
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Sun Oct 17 23:24:22 2021 +0200

    FREEMARKER-199 [freemarker-generator] Add "utah-parser-tool"
---
 .../src/app/config/freemarker-generator.properties |  1 +
 .../utahparser/juniper_bgp_summary_example.txt     | 12 ++++
 .../utahparser/juniper_bgp_summary_template.xml    | 69 ++++++++++++++++++
 .../templates/utahparser/csv/transform.ftl         | 31 ++++++++
 .../templates/utahparser/json/transform.ftl        | 19 +++++
 .../freemarker/generator/cli/ExamplesTest.java     | 11 +++
 freemarker-generator-tools/pom.xml                 |  6 ++
 .../generator/tools/commonscsv/CommonsCSVTool.java |  6 ++
 .../commonscsv/impl/CommonsCSVPrinterFacade.java   | 12 ++++
 .../generator/tools/utahparser/UtahParserTool.java | 56 +++++++++++++++
 .../tools/utahparser/impl/ParserWrapper.java       | 82 ++++++++++++++++++++++
 .../utahparser/juniper_bgp_summary_example.txt     | 12 ++++
 .../utahparser/juniper_bgp_summary_template.xml    | 69 ++++++++++++++++++
 .../tools/utahparser/UtahParserToolTest.java       | 68 ++++++++++++++++++
 14 files changed, 454 insertions(+)

diff --git a/freemarker-generator-cli/src/app/config/freemarker-generator.properties b/freemarker-generator-cli/src/app/config/freemarker-generator.properties
index 25b7029..d65937b 100644
--- a/freemarker-generator-cli/src/app/config/freemarker-generator.properties
+++ b/freemarker-generator-cli/src/app/config/freemarker-generator.properties
@@ -34,6 +34,7 @@ freemarker.tools.jsonpath=org.apache.freemarker.generator.tools.jsonpath.JsonPat
 freemarker.tools.jsoup=org.apache.freemarker.generator.tools.jsoup.JsoupTool
 freemarker.tools.properties=org.apache.freemarker.generator.tools.properties.PropertiesTool
 freemarker.tools.system=org.apache.freemarker.generator.tools.system.SystemTool
+freemarker.tools.utahparser=org.apache.freemarker.generator.tools.utahparser.UtahParserTool
 freemarker.tools.uuid=org.apache.freemarker.generator.tools.uuid.UUIDTool
 freemarker.tools.xml=org.apache.freemarker.generator.tools.xml.XmlTool
 freemarker.tools.yaml=org.apache.freemarker.generator.tools.snakeyaml.SnakeYamlTool
diff --git a/freemarker-generator-cli/src/app/examples/data/text/utahparser/juniper_bgp_summary_example.txt b/freemarker-generator-cli/src/app/examples/data/text/utahparser/juniper_bgp_summary_example.txt
new file mode 100644
index 0000000..c4a209e
--- /dev/null
+++ b/freemarker-generator-cli/src/app/examples/data/text/utahparser/juniper_bgp_summary_example.txt
@@ -0,0 +1,12 @@
+Groups: 3 Peers: 3 Down peers: 0
+Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
+inet.0               947        310          0          0          0          0
+inet6.0              849        807          0          0          0          0
+Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Damped...
+10.247.68.182         65550     131725   28179233       0      11     6w3d17h Establ
+  inet.0: 4/5/1
+  inet6.0: 0/0/0
+10.254.166.246        65550     136159   29104942       0       0      6w5d6h Establ
+  inet.0: 0/0/0
+  inet6.0: 7/8/1
+192.0.2.100           65551    1269381    1363320       0       1      9w5d6h 1/2/3 4/5/6
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/app/examples/data/text/utahparser/juniper_bgp_summary_template.xml b/freemarker-generator-cli/src/app/examples/data/text/utahparser/juniper_bgp_summary_template.xml
new file mode 100644
index 0000000..bb1093a
--- /dev/null
+++ b/freemarker-generator-cli/src/app/examples/data/text/utahparser/juniper_bgp_summary_template.xml
@@ -0,0 +1,69 @@
+<config>
+    <searches>
+
+        <!-- in this case, we have a CSV (space delimited file) so we define the line once, and then reuse it over
+        and again for each value -->
+        <search id="QUERY-LINE"><![CDATA[\s*{ipAddress}\s+{numbers}\s+{numbers}\s+{numbers}\s+{numbers}\s+{numbers}\s+{numbersThenText}]]></search>
+
+
+        <search id="inetInline"><![CDATA[{inet} {inet}]]></search>
+        <search id="inet4"><![CDATA[inet.0:\s*{inet}]]></search>
+        <search id="inet6"><![CDATA[inet6.0:\s*{inet}]]></search>
+        <search id="inet"><![CDATA[{numbers}/{numbers}/{numbers}]]></search>
+
+        <!-- Some rules for finding text, to make the values a little easier below -->
+        <search id="numbers"><![CDATA[(\d+)]]></search>
+        <search id="numbersThenText"><![CDATA[(\d+\S+)]]></search>
+        <search id="string"><![CDATA[(\S+?)]]></search>
+        <search id="ipAddress"><![CDATA[(\d+(\.\d+){3})]]></search>
+        <search id="EOL"><![CDATA[[\n\r]]]></search>
+    </searches>
+
+    <!-- the record starts with a line with an ip address and ends with either an inet6 line, or where the ids are at
+    the end of the line-->
+    <delim retain="true">{ipAddress}.*(\/\d+)\s*{EOL}</delim>
+    <delim>\s*({inet6})</delim>
+
+    <!--
+    This is the last line of the header
+     -->
+    <header-delim><![CDATA[Peer\s+AS\s+InPkt]]></header-delim>
+
+    <!--
+    Files look like this:
+
+    10.247.68.182         65550     131725   28179233       0      11     6w3d17h Establ
+      inet.0: 4/5/1
+      inet6.0: 0/0/0
+
+      or
+
+    192.0.2.100           65551    1269381    1363320       0       1      9w5d6h 2/3/0 0/0/0
+  -->
+    <values>
+        <!-- here we reuse the line pattern, only we pull out different group values -->
+        <value id="remoteIp" group="1"><![CDATA[{QUERY-LINE}]]></value>
+        <value id="uptime" group="8"><![CDATA[{QUERY-LINE}]]></value>
+
+        <!-- here we check for values in the inet* lines and use these -->
+        <value id="activeV4" group="1"><![CDATA[{inet4}]]></value>
+        <value id="receivedV4" group="2"><![CDATA[{inet4}]]></value>
+        <value id="accepted_V4" group="3"><![CDATA[{inet4}]]></value>
+
+        <value id="activeV6" group="1"><![CDATA[{inet6}]]></value>
+        <value id="receivedV6" group="2"><![CDATA[{inet6}]]></value>
+        <value id="accepted_V6" group="3"><![CDATA[{inet6}]]></value>
+
+        <!--
+        here we check for values at the end of the query line, and use these
+         NOTE: since we only set non-null values, these will not overwrite any values set above
+        -->
+        <value id="activeV4" group="9"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="receivedV4" group="10"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="accepted_V4" group="11"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="activeV6" group="12"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="receivedV6" group="13"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="accepted_V6" group="14"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+
+    </values>
+</config>
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/app/examples/templates/utahparser/csv/transform.ftl b/freemarker-generator-cli/src/app/examples/templates/utahparser/csv/transform.ftl
new file mode 100644
index 0000000..5063619
--- /dev/null
+++ b/freemarker-generator-cli/src/app/examples/templates/utahparser/csv/transform.ftl
@@ -0,0 +1,31 @@
+<#--
+  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.
+-->
+<#-- Setup Utah-Parser -->
+<#assign conf =  tools.utahparser.getConfig(dataSources[0])>
+<#assign parser = tools.utahparser.getParser(conf, dataSources[1])>
+<#assign records = parser.toList()>
+<#assign firstRecord = records[0]!{}>
+<#-- Setup CSVPrinter  -->
+<#assign csvHeaders = tools.csv.extractHeaders(firstRecord)>
+<#assign cvsFormat = tools.csv.formats.EXCEL.withHeader(csvHeaders).withDelimiter(';')>
+<#assign csvPrinter = tools.csv.printer(cvsFormat)>
+<#-- Print records as CSV  -->
+<#compress>
+    <#list records as record>
+        ${csvPrinter.printRecord(record, csvHeaders)}
+    </#list>
+</#compress>
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/app/examples/templates/utahparser/json/transform.ftl b/freemarker-generator-cli/src/app/examples/templates/utahparser/json/transform.ftl
new file mode 100644
index 0000000..4c2a53e
--- /dev/null
+++ b/freemarker-generator-cli/src/app/examples/templates/utahparser/json/transform.ftl
@@ -0,0 +1,19 @@
+<#--
+  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.
+-->
+<#assign conf =  tools.utahparser.getConfig(dataSources[0])>
+<#assign parser = tools.utahparser.getParser(conf, dataSources[1])>
+${tools.gson.toJson(parser.toList())}
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
index 0aa2399..687925f 100644
--- a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
@@ -126,6 +126,17 @@ public class ExamplesTest extends AbstractMainTest {
     }
 
     @Test
+    public void shouldRunUtahParserExamples() throws IOException {
+        assertValid(execute("-t src/app/examples/templates/utahparser/csv/transform.ftl " +
+                "src/app/examples/data/text/utahparser/juniper_bgp_summary_template.xml " +
+                "src/app/examples/data/text/utahparser/juniper_bgp_summary_example.txt"));
+
+        assertValid(execute("-t src/app/examples/templates/utahparser/json/transform.ftl " +
+                "src/app/examples/data/text/utahparser/juniper_bgp_summary_template.xml " +
+                "src/app/examples/data/text/utahparser/juniper_bgp_summary_example.txt"));
+    }
+
+    @Test
     public void shouldRunInteractiveTemplateExamples() throws IOException {
         assertValid(execute("-i ${tools.jsonpath.parse(dataSources[0]).read(\"$.info.title\")} src/app/examples/data/json/swagger-spec.json"));
         assertValid(execute("-i ${tools.xml.parse(dataSources[0])[\"recipients/person[1]/name\"]} src/app/examples/data/xml/recipients.xml"));
diff --git a/freemarker-generator-tools/pom.xml b/freemarker-generator-tools/pom.xml
index 590246f..04ca3f1 100644
--- a/freemarker-generator-tools/pom.xml
+++ b/freemarker-generator-tools/pom.xml
@@ -129,6 +129,12 @@
             <artifactId>snakeyaml</artifactId>
             <version>1.28</version>
         </dependency>
+        <!-- UtahPrserTool -->
+        <dependency>
+            <groupId>com.sonalake</groupId>
+            <artifactId>utah-parser</artifactId>
+            <version>1.0.2</version>
+        </dependency>
         <!-- Testing -->
         <dependency>
             <groupId>junit</groupId>
diff --git a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
index 12c757d..519c171 100644
--- a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
+++ b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
@@ -33,6 +33,7 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -98,6 +99,11 @@ public class CommonsCSVTool {
         return new CommonsCSVPrinterFacade(csvFormat);
     }
 
+    public String[] extractHeaders(Map<String, Object> map) {
+        final Set<String> keySet = map.keySet();
+        return keySet.toArray(new String[0]);
+    }
+
     /**
      * Extract the list of unique values (keys) of the column "name".
      *
diff --git a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/impl/CommonsCSVPrinterFacade.java b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/impl/CommonsCSVPrinterFacade.java
index 9909d17..958dbdb 100644
--- a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/impl/CommonsCSVPrinterFacade.java
+++ b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/impl/CommonsCSVPrinterFacade.java
@@ -25,6 +25,9 @@ import java.io.IOException;
 import java.io.StringWriter;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Wrap <code>CSVPrinter</code> so each print method returns
@@ -91,6 +94,15 @@ public class CommonsCSVPrinterFacade implements Flushable, Closeable {
         return getOutput();
     }
 
+    public String printRecord(Map<String, Object> map, String[] headers) throws IOException {
+        final List<String> values = new ArrayList<>(headers.length);
+        for (String header : headers) {
+            final Object value = map.get(header);
+            values.add(value != null ? value.toString() : "");
+        }
+        return printRecord(values);
+    }
+
     private String getOutput() {
         writer.flush();
         final String output = writer.getBuffer().toString();
diff --git a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/utahparser/UtahParserTool.java b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/utahparser/UtahParserTool.java
new file mode 100644
index 0000000..87ceb0d
--- /dev/null
+++ b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/utahparser/UtahParserTool.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.freemarker.generator.tools.utahparser;
+
+import com.sonalake.utah.Parser;
+import com.sonalake.utah.config.Config;
+import com.sonalake.utah.config.ConfigLoader;
+import org.apache.freemarker.generator.base.datasource.DataSource;
+import org.apache.freemarker.generator.base.datasource.DataSourceLoaderFactory;
+import org.apache.freemarker.generator.tools.utahparser.impl.ParserWrapper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class UtahParserTool {
+
+    public Config getConfig(String source) {
+        return this.getConfig(DataSourceLoaderFactory.create().load(source));
+    }
+
+    public Config getConfig(DataSource dataSource) {
+        try (InputStream is = dataSource.getUnsafeInputStream()) {
+            return loadConfig(is);
+        } catch (IOException var16) {
+            throw new RuntimeException("Failed to load parser configuration: " + dataSource, var16);
+        }
+    }
+
+    public ParserWrapper getParser(Config config, DataSource dataSource) {
+        final InputStreamReader is = new InputStreamReader(dataSource.getInputStream());
+        return new ParserWrapper(Parser.parse(config, is));
+    }
+
+    public String toString() {
+        return "Parse semi-structured text using regular expressions (see https://github.com/sonalake/utah-parser)";
+    }
+
+    private static Config loadConfig(InputStream is) throws IOException {
+        return new ConfigLoader().loadConfig(new InputStreamReader(is));
+    }
+}
\ No newline at end of file
diff --git a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/utahparser/impl/ParserWrapper.java b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/utahparser/impl/ParserWrapper.java
new file mode 100644
index 0000000..585f8f8
--- /dev/null
+++ b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/utahparser/impl/ParserWrapper.java
@@ -0,0 +1,82 @@
+/*
+ * 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.freemarker.generator.tools.utahparser.impl;
+
+import com.sonalake.utah.Parser;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Wraps the <code>com.sonalake.utah.Parser</code> to provide convenience
+ * methods such support of iterators.
+ */
+public class ParserWrapper implements Iterable<Map<String, String>> {
+
+    /** The wrapped parser instance */
+    private final Parser parser;
+
+    public ParserWrapper(Parser parser) {
+        this.parser = requireNonNull(parser);
+    }
+
+    @Override
+    public Iterator<Map<String, String>> iterator() {
+        return new RecordsIterator(parser);
+    }
+
+    public List<Map<String, String>> toList() {
+        final List<Map<String, String>> result = new ArrayList<>();
+        for (Map<String, String> record : this) {
+            result.add(record);
+        }
+        return result;
+    }
+
+    private static final class RecordsIterator implements Iterator<Map<String, String>> {
+
+        private final Parser parser;
+        private Map<String, String> nextRecord;
+
+        // constructor
+        RecordsIterator(Parser parser) {
+            this.parser = requireNonNull(parser);
+            this.nextRecord = parser.next();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return nextRecord != null;
+        }
+
+        @Override
+        public Map<String, String> next() {
+            if (nextRecord == null) {
+                throw new NoSuchElementException();
+            }
+
+            final Map<String, String> currentRecord = nextRecord;
+            nextRecord = parser.next();
+            return currentRecord;
+        }
+    }
+}
\ No newline at end of file
diff --git a/freemarker-generator-tools/src/test/data/utahparser/juniper_bgp_summary_example.txt b/freemarker-generator-tools/src/test/data/utahparser/juniper_bgp_summary_example.txt
new file mode 100644
index 0000000..c4a209e
--- /dev/null
+++ b/freemarker-generator-tools/src/test/data/utahparser/juniper_bgp_summary_example.txt
@@ -0,0 +1,12 @@
+Groups: 3 Peers: 3 Down peers: 0
+Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
+inet.0               947        310          0          0          0          0
+inet6.0              849        807          0          0          0          0
+Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Damped...
+10.247.68.182         65550     131725   28179233       0      11     6w3d17h Establ
+  inet.0: 4/5/1
+  inet6.0: 0/0/0
+10.254.166.246        65550     136159   29104942       0       0      6w5d6h Establ
+  inet.0: 0/0/0
+  inet6.0: 7/8/1
+192.0.2.100           65551    1269381    1363320       0       1      9w5d6h 1/2/3 4/5/6
\ No newline at end of file
diff --git a/freemarker-generator-tools/src/test/data/utahparser/juniper_bgp_summary_template.xml b/freemarker-generator-tools/src/test/data/utahparser/juniper_bgp_summary_template.xml
new file mode 100644
index 0000000..bb1093a
--- /dev/null
+++ b/freemarker-generator-tools/src/test/data/utahparser/juniper_bgp_summary_template.xml
@@ -0,0 +1,69 @@
+<config>
+    <searches>
+
+        <!-- in this case, we have a CSV (space delimited file) so we define the line once, and then reuse it over
+        and again for each value -->
+        <search id="QUERY-LINE"><![CDATA[\s*{ipAddress}\s+{numbers}\s+{numbers}\s+{numbers}\s+{numbers}\s+{numbers}\s+{numbersThenText}]]></search>
+
+
+        <search id="inetInline"><![CDATA[{inet} {inet}]]></search>
+        <search id="inet4"><![CDATA[inet.0:\s*{inet}]]></search>
+        <search id="inet6"><![CDATA[inet6.0:\s*{inet}]]></search>
+        <search id="inet"><![CDATA[{numbers}/{numbers}/{numbers}]]></search>
+
+        <!-- Some rules for finding text, to make the values a little easier below -->
+        <search id="numbers"><![CDATA[(\d+)]]></search>
+        <search id="numbersThenText"><![CDATA[(\d+\S+)]]></search>
+        <search id="string"><![CDATA[(\S+?)]]></search>
+        <search id="ipAddress"><![CDATA[(\d+(\.\d+){3})]]></search>
+        <search id="EOL"><![CDATA[[\n\r]]]></search>
+    </searches>
+
+    <!-- the record starts with a line with an ip address and ends with either an inet6 line, or where the ids are at
+    the end of the line-->
+    <delim retain="true">{ipAddress}.*(\/\d+)\s*{EOL}</delim>
+    <delim>\s*({inet6})</delim>
+
+    <!--
+    This is the last line of the header
+     -->
+    <header-delim><![CDATA[Peer\s+AS\s+InPkt]]></header-delim>
+
+    <!--
+    Files look like this:
+
+    10.247.68.182         65550     131725   28179233       0      11     6w3d17h Establ
+      inet.0: 4/5/1
+      inet6.0: 0/0/0
+
+      or
+
+    192.0.2.100           65551    1269381    1363320       0       1      9w5d6h 2/3/0 0/0/0
+  -->
+    <values>
+        <!-- here we reuse the line pattern, only we pull out different group values -->
+        <value id="remoteIp" group="1"><![CDATA[{QUERY-LINE}]]></value>
+        <value id="uptime" group="8"><![CDATA[{QUERY-LINE}]]></value>
+
+        <!-- here we check for values in the inet* lines and use these -->
+        <value id="activeV4" group="1"><![CDATA[{inet4}]]></value>
+        <value id="receivedV4" group="2"><![CDATA[{inet4}]]></value>
+        <value id="accepted_V4" group="3"><![CDATA[{inet4}]]></value>
+
+        <value id="activeV6" group="1"><![CDATA[{inet6}]]></value>
+        <value id="receivedV6" group="2"><![CDATA[{inet6}]]></value>
+        <value id="accepted_V6" group="3"><![CDATA[{inet6}]]></value>
+
+        <!--
+        here we check for values at the end of the query line, and use these
+         NOTE: since we only set non-null values, these will not overwrite any values set above
+        -->
+        <value id="activeV4" group="9"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="receivedV4" group="10"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="accepted_V4" group="11"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="activeV6" group="12"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="receivedV6" group="13"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+        <value id="accepted_V6" group="14"><![CDATA[{QUERY-LINE}\s*{inetInline}]]></value>
+
+    </values>
+</config>
\ No newline at end of file
diff --git a/freemarker-generator-tools/src/test/java/org/apache/freemarker/generator/tools/utahparser/UtahParserToolTest.java b/freemarker-generator-tools/src/test/java/org/apache/freemarker/generator/tools/utahparser/UtahParserToolTest.java
new file mode 100644
index 0000000..4e742bb
--- /dev/null
+++ b/freemarker-generator-tools/src/test/java/org/apache/freemarker/generator/tools/utahparser/UtahParserToolTest.java
@@ -0,0 +1,68 @@
+package org.apache.freemarker.generator.tools.utahparser;
+
+import com.sonalake.utah.config.Config;
+import org.apache.freemarker.generator.base.datasource.DataSource;
+import org.apache.freemarker.generator.base.datasource.DataSourceFactory;
+import org.apache.freemarker.generator.tools.utahparser.impl.ParserWrapper;
+import org.junit.Test;
+
+import java.io.File;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+
+public class UtahParserToolTest {
+
+    private final static String EXAMPLE_FILE_NAME = "src/test/data/utahparser/juniper_bgp_summary_example.txt";
+    private final static String TEMPLATE_FILE_NAME = "src/test/data/utahparser/juniper_bgp_summary_template.xml";
+
+    @Test
+
+    public void shallLoadConfFromFile() {
+        final Config config = utahParserTool().getConfig(TEMPLATE_FILE_NAME);
+
+        assertNotNull(config);
+        assertTrue(config.isDelimiterValid());
+    }
+
+    @Test
+    public void shallLoadConfFromDataSource() {
+        final DataSource dataSource = dataSource(TEMPLATE_FILE_NAME);
+        final Config config = utahParserTool().getConfig(dataSource);
+
+        assertNotNull(config);
+        assertTrue(config.isDelimiterValid());
+    }
+
+    @Test
+    public void shallGetParserInstance() {
+        final DataSource dataSource = dataSource(EXAMPLE_FILE_NAME);
+        final UtahParserTool utahParserTool = utahParserTool();
+        final Config config = utahParserTool.getConfig(TEMPLATE_FILE_NAME);
+        final ParserWrapper parser = utahParserTool.getParser(config, dataSource);
+
+        assertNotNull(parser);
+        assertNotNull(parser.iterator());
+    }
+
+    @Test
+    public void shallParseAllData() {
+        final DataSource dataSource = dataSource(EXAMPLE_FILE_NAME);
+        final UtahParserTool utahParserTool = utahParserTool();
+        final Config config = utahParserTool.getConfig(TEMPLATE_FILE_NAME);
+        final ParserWrapper parser = utahParserTool.getParser(config, dataSource);
+
+        assertEquals(3, parser.toList().size());
+    }
+
+    private static UtahParserTool utahParserTool() {
+        return new UtahParserTool();
+    }
+
+    private static DataSource dataSource(String fileName) {
+        return DataSourceFactory.fromFile(new File(fileName), UTF_8);
+    }
+
+}