From 4bb7cce55240c4aa50b19504e867b3a17e59500c Mon Sep 17 00:00:00 2001 From: Rikard Pavelic Date: Fri, 22 Apr 2022 15:38:07 +0200 Subject: [PATCH] Improvements and new examples Make Android work without Xalan (use stax instead) Remove usage of thread locals in AlternativeProperty (use references instead) Show how to get streaming behavior with iterator and how to stream XML. Pass xml instruction via XML instead of tag metadata. --- Advanced/CsvStreaming/Readme.md | 7 - Advanced/README.md | 7 +- Advanced/Streaming/Readme.md | 9 + .../Streaming.csproj} | 11 +- .../packages.config | 0 Advanced/{CsvStreaming => Streaming}/pom.xml | 4 +- .../{CsvStreaming => Streaming}/result.csv | 0 Advanced/Streaming/result.xml | 165 ++++++++++++++++++ .../src/Program.cs | 108 +++++++++--- .../templater/example/StreamingExample.java} | 133 +++++++++++--- .../template/input.csv | 0 Advanced/Streaming/template/input.xml | 15 ++ Advanced/TemplaterServer/Dockerfile | 2 +- Beginner/AndroidExample/app/build.gradle | 7 +- .../hr/ngs/templater/example/Templater.java | 10 +- Beginner/AndroidExample/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- Beginner/DataSet (.NET)/Readme.md | 10 +- .../AlternativeProperty/src/Program.cs | 12 +- .../ngs/templater/example/FieldsExample.java | 32 ++-- Intermediate/HtmlToExcel/result.xlsx | Bin 9434 -> 10814 bytes Intermediate/HtmlToExcel/src/Program.cs | 26 ++- .../templater/example/HtmlExcelExample.java | 41 ++++- .../HtmlToExcel/template/Document.xlsx | Bin 9286 -> 10636 bytes Intermediate/HtmlToWord/Readme.md | 2 + Intermediate/HtmlToWord/src/Program.cs | 5 +- .../templater/example/HtmlWordExample.java | 4 +- .../HtmlToWord/template/template.docx | Bin 12199 -> 12197 bytes Intermediate/README.md | 2 +- README.md | 5 +- RunAll/Program.cs | 2 +- RunAll/RunAll.csproj | 8 +- Templater.sln | 24 +-- pom.xml | 4 +- src/test/java/DemoTests.java | 5 +- 35 files changed, 529 insertions(+), 137 deletions(-) delete mode 100644 Advanced/CsvStreaming/Readme.md create mode 100644 Advanced/Streaming/Readme.md rename Advanced/{CsvStreaming/CsvStreaming.csproj => Streaming/Streaming.csproj} (91%) rename Advanced/{CsvStreaming => Streaming}/packages.config (100%) rename Advanced/{CsvStreaming => Streaming}/pom.xml (94%) rename Advanced/{CsvStreaming => Streaming}/result.csv (100%) create mode 100644 Advanced/Streaming/result.xml rename Advanced/{CsvStreaming => Streaming}/src/Program.cs (50%) rename Advanced/{CsvStreaming/src/main/java/hr/ngs/templater/example/CsvStreamingExample.java => Streaming/src/main/java/hr/ngs/templater/example/StreamingExample.java} (51%) rename Advanced/{CsvStreaming => Streaming}/template/input.csv (100%) create mode 100644 Advanced/Streaming/template/input.xml diff --git a/Advanced/CsvStreaming/Readme.md b/Advanced/CsvStreaming/Readme.md deleted file mode 100644 index ef12dd31..00000000 --- a/Advanced/CsvStreaming/Readme.md +++ /dev/null @@ -1,7 +0,0 @@ -## Streaming of large CSV documents - -Streaming in Templater is done by multiple calls to process API. -This allows to Templater to flush the content of populated stream and reuse memory in next call to process API. - -Streaming can be done only up to row without tags. This means that first non-streaming tags should be processed (if there are any) -and then streaming tags can be processed which will perform flushing. \ No newline at end of file diff --git a/Advanced/README.md b/Advanced/README.md index 2b392b6c..0cd69b5c 100644 --- a/Advanced/README.md +++ b/Advanced/README.md @@ -44,11 +44,12 @@ Consuming embedded CSV or Excel table via Power Query (Requires Excel 2010+) [template](PowerQuery/template/PowerQuery.xlsx?raw=true) - [result](PowerQuery/result.xlsx?raw=true) -### [CSV streaming](CsvStreaming/Readme.md) +### [CSV streaming](Streaming/Readme.md) -Stream CSV while processing to support huge exports +Stream CSV/XML while processing to support huge exports -[template](CsvStreaming/template/input.csv) - [result](CsvStreaming/result.csv) +[csv template](Streaming/template/input.csv) - [result](Streaming/result.csv) +[xml template](Streaming/template/input.xml) - [result](Streaming/result.xml) ### [Various JSON examples](TemplaterServer/Readme.md) diff --git a/Advanced/Streaming/Readme.md b/Advanced/Streaming/Readme.md new file mode 100644 index 00000000..cf1f4946 --- /dev/null +++ b/Advanced/Streaming/Readme.md @@ -0,0 +1,9 @@ +## Streaming of large documents + +Streaming in Templater is supported out of the box if streaming type is used (ResultSet/Iterator/Enumerator). +Alternatively streaming can be simulated manually by multiple calls to process API. + +Both methods allows Templater to flush the content of populated stream and reuse memory in next call to process API. + +Streaming can be done only up to row without tags. This means that first non-streaming tags should be processed (if there are any) +and then streaming tags can be processed which will perform flushing. \ No newline at end of file diff --git a/Advanced/CsvStreaming/CsvStreaming.csproj b/Advanced/Streaming/Streaming.csproj similarity index 91% rename from Advanced/CsvStreaming/CsvStreaming.csproj rename to Advanced/Streaming/Streaming.csproj index 305735c7..1920dd92 100644 --- a/Advanced/CsvStreaming/CsvStreaming.csproj +++ b/Advanced/Streaming/Streaming.csproj @@ -8,8 +8,8 @@ {5BB2AABB-A28F-404F-8C37-DBE122E893F5} Exe Properties - CsvStreaming - CsvStreaming + Streaming + Streaming v4.0 Client 512 @@ -39,7 +39,7 @@ ..\..\packages\DotNetZip.1.13.0\lib\net40\DotNetZip.dll True - + ..\..\packages\Templater.7.0.0\lib\Net40\NGS.Templater.dll False @@ -65,6 +65,11 @@ Always + + + Always + + diff --git a/Advanced/CsvStreaming/packages.config b/Advanced/Streaming/packages.config similarity index 100% rename from Advanced/CsvStreaming/packages.config rename to Advanced/Streaming/packages.config diff --git a/Advanced/CsvStreaming/pom.xml b/Advanced/Streaming/pom.xml similarity index 94% rename from Advanced/CsvStreaming/pom.xml rename to Advanced/Streaming/pom.xml index f86e46f6..a980f2b1 100644 --- a/Advanced/CsvStreaming/pom.xml +++ b/Advanced/Streaming/pom.xml @@ -2,10 +2,10 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 hr.ngs.templater.example - csv-streaming-example + streaming-example jar 7.0.0 - CSV streaming + Streaming https://github.com/ngs-doo/TemplaterExamples diff --git a/Advanced/CsvStreaming/result.csv b/Advanced/Streaming/result.csv similarity index 100% rename from Advanced/CsvStreaming/result.csv rename to Advanced/Streaming/result.csv diff --git a/Advanced/Streaming/result.xml b/Advanced/Streaming/result.xml new file mode 100644 index 00000000..aaa9d0ef --- /dev/null +++ b/Advanced/Streaming/result.xml @@ -0,0 +1,165 @@ + + + + + 260 + 27. 07. 2019. + + reference0 + branch0 + + + + 260 + 27. 07. 2019. + + reference1 + branch1 + + + + 260 + 27. 07. 2019. + + reference2 + branch2 + + - + + 260 + 27. 07. 2019. + + reference3 + branch3 + + ... + + 261 + 27. 07. 2019. + + reference4 + branch4 + + IMPORTANT + + 261 + 27. 07. 2019. + + reference5 + branch5 + + REMINDER + + 261 + 27. 07. 2019. + + reference6 + branch6 + + something to look "into later + + 261 + 27. 07. 2019. + + reference7 + branch7 + + special" char, + + 261 + 27. 07. 2019. + + reference8 + branch8 + + + + 261 + 27. 07. 2019. + + reference9 + branch9 + + + + 261 + 27. 07. 2019. + + reference10 + branch10 + + - + + 261 + 27. 07. 2019. + + reference11 + branch11 + + ... + + 262 + 27. 07. 2019. + + reference12 + branch12 + + IMPORTANT + + 262 + 27. 07. 2019. + + reference13 + branch13 + + REMINDER + + 262 + 27. 07. 2019. + + reference14 + branch14 + + something to look "into later + + 262 + 27. 07. 2019. + + reference15 + branch15 + + special" char, + + 262 + 27. 07. 2019. + + reference16 + branch16 + + + + 262 + 27. 07. 2019. + + reference17 + branch17 + + + + 262 + 27. 07. 2019. + + reference18 + branch18 + + - + + 262 + 27. 07. 2019. + + reference19 + branch19 + + ... + + \ No newline at end of file diff --git a/Advanced/CsvStreaming/src/Program.cs b/Advanced/Streaming/src/Program.cs similarity index 50% rename from Advanced/CsvStreaming/src/Program.cs rename to Advanced/Streaming/src/Program.cs index fb5bcdad..284ff399 100644 --- a/Advanced/CsvStreaming/src/Program.cs +++ b/Advanced/Streaming/src/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; @@ -8,7 +9,7 @@ using Ionic.Zip; using NGS.Templater; -namespace CsvStreaming +namespace Streaming { public class Program { @@ -45,7 +46,7 @@ struct StreamingRow public string verifiedBy; public DateTime verifiedOn; - public StreamingRow(DataTableReader reader) + public StreamingRow(IDataReader reader) { id = reader.GetInt32(0); amount = reader.GetDecimal(1); @@ -59,6 +60,22 @@ public StreamingRow(DataTableReader reader) verifiedBy = reader.IsDBNull(9) ? null : reader.GetString(9); verifiedOn = reader.GetDateTime(10); } + + public class ReaderIterator : IEnumerator + { + private readonly IDataReader Reader; + + public ReaderIterator(IDataReader reader) + { + this.Reader = reader; + } + + public StreamingRow Current { get { return new StreamingRow(Reader); } } + object IEnumerator.Current { get { return Current; } } + public bool MoveNext() { return Reader.Read(); } + public void Reset() { } + public void Dispose() { } + } } public static void Main(string[] args) @@ -97,42 +114,77 @@ public static void Main(string[] args) startTimestamp.AddMinutes(i) ); } - var reader = table.CreateDataReader(); - var config = Configuration.Builder.Include(Quoter); + var reader1 = table.CreateDataReader(); + var reader2 = table.CreateDataReader(); + var reader3 = table.CreateDataReader(); + var csvConfig = Configuration.Builder.Include(Quoter); //we need quoting as we are simulating CSV + var xmlConfig = Configuration.Builder; //we don't need quoting as XML is natively supported //if we are using a culture which has comma as decimal separator, change the output to dot //we could apply this always, but it adds a bit of overhead, so let's apply it conditionally if (Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator.Contains(",")) - config.Include(NumberAsDot); + { + csvConfig.Include(NumberAsDot); + xmlConfig.Include(NumberAsDot); + } + csvConfig.Streaming(50000);//by default streaming is 16k, lets leave the default for xml + var csvFactory = csvConfig.Build(); + var xmlFactory = xmlConfig.Build(); //for example purposes we will stream it a zip file using (var zip = new ZipOutputStream("output.zip")) { - zip.PutNextEntry("output.csv"); - using (var doc = config.Build().Open(File.OpenRead("template/input.csv"), "csv", zip)) + zip.PutNextEntry("manual.csv"); + var sw = Stopwatch.StartNew(); + ManualStreaming(reader1, csvFactory, zip); + Console.WriteLine("manual csv took: " + sw.ElapsedMilliseconds); + zip.PutNextEntry("automatic.csv"); + sw = Stopwatch.StartNew(); + AutomaticStreaming(reader2, csvFactory, "csv", zip); + Console.WriteLine("automatic csv took: " + sw.ElapsedMilliseconds); + zip.PutNextEntry("data.xml"); + sw = Stopwatch.StartNew(); + AutomaticStreaming(reader3, xmlFactory, "xml", zip); + Console.WriteLine("automatic xml took: " + sw.ElapsedMilliseconds); + } + Process.Start(new ProcessStartInfo("output.zip") { UseShellExecute = true }); + } + + private static void ManualStreaming(IDataReader reader, IDocumentFactory factory, ZipOutputStream zip) + { + using (var doc = factory.Open(File.OpenRead("template/input.csv"), "csv", zip)) + { + //streaming processing assumes we have only a single collection, which means we first need to process all other tags + doc.Process(new { filter = new { date = "All", user = "All" } }); + //to do a streaming processing we need to process collection in chunks + var chunk = new List(50000); + var hasData = reader.Read(); + while (hasData) { - //streaming processing assumes we have only a single collection, which means we first need to process all other tags - doc.Process(new { filter = new { date = "All", user = "All" } }); - //to do a streaming processing we need to process collection in chunks - var chunk = new List(50000); - var hasData = reader.Read(); - while (hasData) + //one way of doing streaming is first duplicating the template row (context) + doc.Templater.Resize(doc.Templater.Tags, 2); + //and then process that row with all known data + //this way we will have additional row to process (or remove) later + do { - //one way of doing streaming is first duplicating the template row (context) - doc.Templater.Resize(doc.Templater.Tags, 2); - //and then process that row with all known data - //this way we will have additional row to process (or remove) later - do - { - chunk.Add(new StreamingRow(reader)); - hasData = reader.Read(); - } while (chunk.Count < 50000 && hasData); - doc.Process(new { data = chunk }); - chunk.Clear(); - } - //remove remaining rows - doc.Templater.Resize(doc.Templater.Tags, 0); + chunk.Add(new StreamingRow(reader)); + hasData = reader.Read(); + } while (chunk.Count < 50000 && hasData); + doc.Process(new { data = chunk }); + chunk.Clear(); } + //remove remaining rows + doc.Templater.Resize(doc.Templater.Tags, 0); + } + } + + private static void AutomaticStreaming(IDataReader reader, IDocumentFactory factory, string extension, ZipOutputStream zip) + { + using (var doc = factory.Open(File.OpenRead("template/input." + extension), extension, zip)) + { + //we still want to make sure all non collection tags are processed first (or they are at the end of document) + doc.Process(new { filter = new { date = "All", user = "All" } }); + //for streaming lets just pass enumerator for processing + doc.Process(new { data = new StreamingRow.ReaderIterator(reader) }); } - Process.Start(new ProcessStartInfo("output.zip") { UseShellExecute = true }); } } } diff --git a/Advanced/CsvStreaming/src/main/java/hr/ngs/templater/example/CsvStreamingExample.java b/Advanced/Streaming/src/main/java/hr/ngs/templater/example/StreamingExample.java similarity index 51% rename from Advanced/CsvStreaming/src/main/java/hr/ngs/templater/example/CsvStreamingExample.java rename to Advanced/Streaming/src/main/java/hr/ngs/templater/example/StreamingExample.java index 8743a696..b9099d11 100644 --- a/Advanced/CsvStreaming/src/main/java/hr/ngs/templater/example/CsvStreamingExample.java +++ b/Advanced/Streaming/src/main/java/hr/ngs/templater/example/StreamingExample.java @@ -1,6 +1,7 @@ package hr.ngs.templater.example; import hr.ngs.templater.Configuration; +import hr.ngs.templater.DocumentFactory; import hr.ngs.templater.DocumentFactoryBuilder; import hr.ngs.templater.TemplateDocument; @@ -13,10 +14,11 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; +import java.util.Iterator; import java.util.Locale; import java.util.zip.*; -public class CsvStreamingExample { +public class StreamingExample { static class Quoter implements DocumentFactoryBuilder.LowLevelReplacer { @@ -58,7 +60,7 @@ public static class StreamingRow { public String verifiedBy; public Timestamp verifiedOn; - public StreamingRow(ResultSet rs) throws SQLException { + public StreamingRow(ResultSet rs) throws SQLException { id = rs.getInt(1); amount = rs.getBigDecimal(2); date = rs.getDate(3); @@ -71,10 +73,35 @@ public StreamingRow(ResultSet rs) throws SQLException { verifiedBy = rs.getString(10); verifiedOn = rs.getTimestamp(11); } + + public static class RsIterator implements Iterator { + private final ResultSet rs; + private boolean hasNext; + + public RsIterator(ResultSet rs) throws SQLException { + this.rs = rs; + this.hasNext = rs.next(); + } + + @Override + public boolean hasNext() { + return hasNext; + } + + @Override + public StreamingRow next() { + try { + StreamingRow row = new StreamingRow(rs); + hasNext = rs.next(); + return row; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } } public static void main(final String[] args) throws Exception { - InputStream templateStream = CsvStreamingExample.class.getResourceAsStream("/input.csv"); File tmp = File.createTempFile("output", ".zip"); Class.forName("org.hsqldb.jdbcDriver"); @@ -114,41 +141,89 @@ public static void main(final String[] args) throws Exception { ins.setTimestamp(11, new java.sql.Timestamp(startTimestamp.plusMinutes(i / 1000).toInstant().toEpochMilli())); ins.execute(); } - ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM csv_data"); - DocumentFactoryBuilder config = Configuration.builder().include(new Quoter()); + ResultSet rs1 = conn.createStatement().executeQuery("SELECT * FROM csv_data"); + ResultSet rs2 = conn.createStatement().executeQuery("SELECT * FROM csv_data"); + ResultSet rs3 = conn.createStatement().executeQuery("SELECT * FROM csv_data"); + DocumentFactoryBuilder csvConfig = Configuration.builder().include(new Quoter()); + DocumentFactoryBuilder xmlConfig = Configuration.builder(); DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.getDefault()); //if we are using a culture which has comma as decimal separator, change the output to dot //we could apply this always, but it adds a bit of overhead, so let's apply it conditionally if (dfs.getDecimalSeparator() == ',') { - config.include(new NumberAsComma()); + csvConfig.include(new NumberAsComma()); + xmlConfig.include(new NumberAsComma()); } + csvConfig.streaming(50000);//by default streaming is 16k, lets leave the default for xml + DocumentFactory csvFactory = csvConfig.build(); + DocumentFactory xmlFactory = xmlConfig.build(); //we can stream directly into a zipped stream/file ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmp)); - zos.putNextEntry(new ZipEntry("output.csv")); - TemplateDocument doc = config.build().open(templateStream, "csv", zos); - //streaming processing assumes we have only a single collection, which means we first need to process all other tags - doc.process(new Object() { public Object filter = new Object() { public String date = "All"; public String user = "All"; }; }); - //to do a streaming processing we need to process collection in chunks - ArrayList chunk = new ArrayList<>(50000); - boolean hasData = rs.next(); - while (hasData) { - //one way of doing streaming is first duplicating the template row (context) - doc.templater().resize(doc.templater().tags(), 2); - //and then process that row with all known data - //this way we will have additional row to process (or remove) later - do { - chunk.add(new StreamingRow(rs)); - hasData = rs.next(); - } while (chunk.size() < 50000 && hasData); - doc.process(new Object() { public ArrayList data = chunk; }); - chunk.clear(); - } - //remove remaining rows - doc.templater().resize(doc.templater().tags(), 0); - doc.close(); + zos.putNextEntry(new ZipEntry("manual.csv")); + long start = System.currentTimeMillis(); + manualStreaming(rs1, csvFactory, zos); + System.out.println("manual csv took: " + (System.currentTimeMillis() - start)); + zos.putNextEntry(new ZipEntry("automatic.csv")); + start = System.currentTimeMillis(); + automaticStreaming(rs2, csvFactory, "csv", zos); + System.out.println("automatic csv took: " + (System.currentTimeMillis() - start)); + zos.putNextEntry(new ZipEntry("data.xml")); + start = System.currentTimeMillis(); + //by default XML will do many small operations so its much faster to wrap the stream with a buffer + BufferedOutputStream bos = new BufferedOutputStream(zos); + automaticStreaming(rs3, xmlFactory, "xml", bos); + bos.flush(); + System.out.println("automatic xml took: " + (System.currentTimeMillis() - start)); conn.close(); - zos.closeEntry(); zos.close(); Desktop.getDesktop().open(tmp); } + + private static void manualStreaming(ResultSet rs, DocumentFactory factory, OutputStream os) throws SQLException { + InputStream templateStream = StreamingExample.class.getResourceAsStream("/input.csv"); + try (TemplateDocument doc = factory.open(templateStream, "csv", os)) { + //streaming processing assumes we have only a single collection, which means we first need to process all other tags + doc.process(new Object() { + public Object filter = new Object() { + public String date = "All"; + public String user = "All"; + }; + }); + //to do a streaming processing we need to process collection in chunks + ArrayList chunk = new ArrayList<>(50000); + boolean hasData = rs.next(); + while (hasData) { + //one way of doing streaming is first duplicating the template row (context) + doc.templater().resize(doc.templater().tags(), 2); + //and then process that row with all known data + //this way we will have additional row to process (or remove) later + do { + chunk.add(new StreamingRow(rs)); + hasData = rs.next(); + } while (chunk.size() < 50000 && hasData); + doc.process(new Object() { + public ArrayList data = chunk; + }); + chunk.clear(); + } + //remove remaining rows + doc.templater().resize(doc.templater().tags(), 0); + } + } + + private static void automaticStreaming(ResultSet rs, DocumentFactory factory, String extension, OutputStream os) throws SQLException { + InputStream templateStream = StreamingExample.class.getResourceAsStream("/input." + extension); + try (TemplateDocument doc = factory.open(templateStream, extension, os)) { + //we still want to make sure all non collection tags are processed first (or they are at the end of document) + doc.process(new Object() { + public Object filter = new Object() { + public String date = "All"; + public String user = "All"; + }; + }); + //for streaming lets just pass iterator for processing + doc.process(new Object() { + public Iterator data = new StreamingRow.RsIterator(rs); + }); + } + } } diff --git a/Advanced/CsvStreaming/template/input.csv b/Advanced/Streaming/template/input.csv similarity index 100% rename from Advanced/CsvStreaming/template/input.csv rename to Advanced/Streaming/template/input.csv diff --git a/Advanced/Streaming/template/input.xml b/Advanced/Streaming/template/input.xml new file mode 100644 index 00000000..d8d477b9 --- /dev/null +++ b/Advanced/Streaming/template/input.xml @@ -0,0 +1,15 @@ + + + + + + [[data.amount]] + [[data.date]:format] + + [[data.reference]] + [[data.branch]] + + [[data.note]] + + + diff --git a/Advanced/TemplaterServer/Dockerfile b/Advanced/TemplaterServer/Dockerfile index 5cabcfc9..8992961e 100644 --- a/Advanced/TemplaterServer/Dockerfile +++ b/Advanced/TemplaterServer/Dockerfile @@ -6,7 +6,7 @@ ENV TZ=Europe/Zagreb RUN apt update && apt install openjdk-11-jre-headless libreoffice-common libreoffice-java-common libreoffice-writer libreoffice-calc wget -yq -RUN wget -q https://github.com/ngs-doo/TemplaterExamples/releases/download/v6.1.0/templater-server.jar +RUN wget -q https://github.com/ngs-doo/TemplaterExamples/releases/download/v7.0.0/templater-server.jar COPY templater.lic . diff --git a/Beginner/AndroidExample/app/build.gradle b/Beginner/AndroidExample/app/build.gradle index 44483d38..4091b7cf 100644 --- a/Beginner/AndroidExample/app/build.gradle +++ b/Beginner/AndroidExample/app/build.gradle @@ -4,12 +4,16 @@ android { compileSdkVersion 28 defaultConfig { applicationId "hr.ngs.templater.example" - minSdkVersion 19 + minSdkVersion 26 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } buildTypes { release { minifyEnabled false @@ -39,6 +43,7 @@ dependencies { implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:28.0.0' implementation 'hr.ngs.templater:templater:7.0.0' + implementation 'stax:stax:1.2.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/Beginner/AndroidExample/app/src/main/java/hr/ngs/templater/example/Templater.java b/Beginner/AndroidExample/app/src/main/java/hr/ngs/templater/example/Templater.java index c9a9ef45..23478cdd 100644 --- a/Beginner/AndroidExample/app/src/main/java/hr/ngs/templater/example/Templater.java +++ b/Beginner/AndroidExample/app/src/main/java/hr/ngs/templater/example/Templater.java @@ -1,28 +1,24 @@ package hr.ngs.templater.example; -import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import hr.ngs.templater.Configuration; -import hr.ngs.templater.ITemplateDocument; +import hr.ngs.templater.TemplateDocument; public abstract class Templater { public static void createDocument(InputStream template, String extension, OutputStream result, Object ...data) throws IOException { //By default Templater will include Java images in low level plugins. //To avoid missing awt dependency disable low level plugins - //Use custom XML library as Android one does not work for non-trivial stuff - ITemplateDocument document = Configuration.builder() + TemplateDocument document = Configuration.builder() .builtInLowLevelPlugins(false) - .xmlBuilder(new org.apache.xerces.jaxp.DocumentBuilderFactoryImpl(), false) .build().open(template, extension, result); for(Object d : data) { document.process(d); } - document.flush(); + document.close(); template.close(); } } diff --git a/Beginner/AndroidExample/build.gradle b/Beginner/AndroidExample/build.gradle index 160cb824..bc090d89 100644 --- a/Beginner/AndroidExample/build.gradle +++ b/Beginner/AndroidExample/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:4.0.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/Beginner/AndroidExample/gradle/wrapper/gradle-wrapper.properties b/Beginner/AndroidExample/gradle/wrapper/gradle-wrapper.properties index d3b65932..54fb3aa7 100644 --- a/Beginner/AndroidExample/gradle/wrapper/gradle-wrapper.properties +++ b/Beginner/AndroidExample/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Dec 01 09:22:26 CET 2019 +#Tue Apr 19 15:52:59 CEST 2022 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/Beginner/DataSet (.NET)/Readme.md b/Beginner/DataSet (.NET)/Readme.md index bb556072..601f6072 100644 --- a/Beginner/DataSet (.NET)/Readme.md +++ b/Beginner/DataSet (.NET)/Readme.md @@ -57,4 +57,12 @@ In this case to specify background color for a cell, Word uses properties such a -As of v2.5 Templater can use merge-xml metadata as instruction to merge provided XML to the surrounding context. This way we can "append" color to the appropriate place. \ No newline at end of file +As of v2.5 Templater can use merge-xml metadata as instruction to merge provided XML to the surrounding context. This way we can "append" color to the appropriate place. + +As of v7 this merge-xml can be passed directly through XML so there is no need for it in tag metadata. In that case XML would look like: + + + + + + diff --git a/Intermediate/AlternativeProperty/src/Program.cs b/Intermediate/AlternativeProperty/src/Program.cs index 9bae44dc..e39875a9 100644 --- a/Intermediate/AlternativeProperty/src/Program.cs +++ b/Intermediate/AlternativeProperty/src/Program.cs @@ -21,18 +21,18 @@ class MyObject public MyObjectA objectA = new MyObjectA(); public MyObjectB objectB = new MyObjectB(); } - private static ThreadLocal currentRoot = new ThreadLocal(); public static void Main(string[] args) { File.Copy("template/Fields.docx", "Fields.docx", true); + object currentRoot = null; Func missingFormatter = (value, metadata) => { if (metadata.StartsWith("missing(") && value == null) { //path to appropriate field string[] path = metadata.Substring(8, metadata.Length - 9).Split('.'); - object current = currentRoot.Value; + object current = currentRoot; foreach (string p in path) { var f = current.GetType().GetField(p); @@ -44,20 +44,20 @@ public static void Main(string[] args) }; var factory = Configuration.Builder.Include(missingFormatter).Build(); using (var doc = factory.Open("Fields.docx")) - ProcessValue(doc, new MyObject()); + ProcessValue(ref currentRoot, doc, new MyObject()); Process.Start(new ProcessStartInfo("Fields.docx") { UseShellExecute = true }); } - private static void ProcessValue(ITemplateDocument doc, object value) + private static void ProcessValue(ref object currentRoot, ITemplateDocument doc, object value) { try { - currentRoot.Value = value; + currentRoot = value; doc.Process(value); } finally { - currentRoot.Value = null; + currentRoot = null; } } } diff --git a/Intermediate/AlternativeProperty/src/main/java/hr/ngs/templater/example/FieldsExample.java b/Intermediate/AlternativeProperty/src/main/java/hr/ngs/templater/example/FieldsExample.java index 90ae9e7f..2b2e4399 100644 --- a/Intermediate/AlternativeProperty/src/main/java/hr/ngs/templater/example/FieldsExample.java +++ b/Intermediate/AlternativeProperty/src/main/java/hr/ngs/templater/example/FieldsExample.java @@ -11,18 +11,22 @@ public class FieldsExample { static class MyObjectA { public String fieldA = null; } + static class MyObjectB { public String fieldB = "alternative value"; } + static class MyObject { public MyObjectA objectA = new MyObjectA(); public MyObjectB objectB = new MyObjectB(); } static class MissingFormatter implements DocumentFactoryBuilder.Formatter { - private Callable getRoot; - public MissingFormatter(Callable getRoot) { - this.getRoot = getRoot; + //to be able to navigate over non processed object, lets keep reference to entry point + private Object currentRootObject; + + public void setRoot(Object root) { + this.currentRootObject = root; } @Override @@ -31,8 +35,8 @@ public Object format(Object value, String metadata) { try { //path to appropriate field String[] path = metadata.substring(8, metadata.length() - 1).split("\\."); - Object current = getRoot.call(); - for(String p : path) { + Object current = currentRootObject; + for (String p : path) { Field f = current.getClass().getField(p); current = f.get(current); } @@ -44,31 +48,25 @@ public Object format(Object value, String metadata) { } } - private static final ThreadLocal currentRoot = new ThreadLocal(); - public static void main(final String[] args) throws Exception { InputStream templateStream = FieldsExample.class.getResourceAsStream("/Fields.docx"); File tmp = File.createTempFile("fields", ".docx"); FileOutputStream fos = new FileOutputStream(tmp); - DocumentFactory factory = Configuration.builder().include(new MissingFormatter(new Callable() { - @Override - public Object call() { - return currentRoot.get(); - } - })).build(); + MissingFormatter formatter = new MissingFormatter(); + DocumentFactory factory = Configuration.builder().include(formatter).build(); try (TemplateDocument tpl = factory.open(templateStream, "docx", fos)) { - process(tpl, new MyObject()); + process(formatter, tpl, new MyObject()); } fos.close(); Desktop.getDesktop().open(tmp); } - private static void process(TemplateDocument doc, Object value) { + private static void process(MissingFormatter formatter, TemplateDocument doc, Object value) { try { - currentRoot.set(value); + formatter.setRoot(value); // we can keep track of root object in a formatter plugin doc.process(value); } finally { - currentRoot.remove(); + formatter.setRoot(null); } } } diff --git a/Intermediate/HtmlToExcel/result.xlsx b/Intermediate/HtmlToExcel/result.xlsx index 6591cf828900ee1004546b862a7be6771da18ebb..0b362a935ee25df6b9a5de6186f5da52dd24fb6a 100644 GIT binary patch delta 7606 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 delta 6264 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 O6d5NIn27Pu=zjsU?MFWV diff --git a/Intermediate/HtmlToExcel/src/Program.cs b/Intermediate/HtmlToExcel/src/Program.cs index af7b8cd9..7d79e740 100644 --- a/Intermediate/HtmlToExcel/src/Program.cs +++ b/Intermediate/HtmlToExcel/src/Program.cs @@ -74,18 +74,42 @@ public static object ConverterHtml(object value, string metadata) return value; } + enum Color + { + RED = 0, + ORANGE = 1, + YELLOW = 2, + GREEN = 3, + BLUE = 4 + } + + static object ColorToXML(object value, string tag, string[] metadata) + { + if (value is Color) + { + var c = (Color)value; + //we need to know the location of conversion table in Excel (this could be provided as argument if needed) + var t = new XElement(XName.Get("t", "http://schemas.openxmlformats.org/spreadsheetml/2006/main")); + t.SetAttributeValue("templater-cell-style", "Colors!A" + (2 + (int)c)); + return t; + } + return value; + } + public static void Main(string[] args) { File.Copy("template/Document.xlsx", "Html.xlsx", true); var factory = Configuration.Builder .Include(ConverterHtml) + .Include(ColorToXML) .Build(); using (var doc = factory.Open("Html.xlsx")) { doc.Process(new { html = "

My simple bold text in red!

", - numbers = new[] { new Number(100), new Number(-100), new Number(10) } + numbers = new[] { new Number(100), new Number(-100), new Number(10) }, + background = Color.ORANGE }); } Process.Start(new ProcessStartInfo("Html.xlsx") { UseShellExecute = true }); diff --git a/Intermediate/HtmlToExcel/src/main/java/hr/ngs/templater/example/HtmlExcelExample.java b/Intermediate/HtmlToExcel/src/main/java/hr/ngs/templater/example/HtmlExcelExample.java index 7047a514..971b7e39 100644 --- a/Intermediate/HtmlToExcel/src/main/java/hr/ngs/templater/example/HtmlExcelExample.java +++ b/Intermediate/HtmlToExcel/src/main/java/hr/ngs/templater/example/HtmlExcelExample.java @@ -93,6 +93,41 @@ public Object format(Object value, String metadata) { } } + enum Color { + RED(0), + ORANGE(1), + YELLOW(2), + GREEN(3), + BLUE(4); + + public final int value; + + Color(int value) { + this.value = value; + } + } + + private static class ColorToXML implements DocumentFactoryBuilder.LowLevelReplacer { + DocumentBuilder dBuilder; + + ColorToXML(DocumentBuilder dBuilder) { + this.dBuilder = dBuilder; + } + + @Override + public Object replace(Object value, String tag, String[] metadata) { + if (value instanceof Color) { + Color c = (Color) value; + //we need to know the location of conversion table in Excel (this could be provided as argument if needed) + Element t = dBuilder.newDocument().createElement("t"); + t.setAttribute("templater-cell-style", "Colors!A" + (2 + c.value)); + return t; + } + return value; + } + } + + public static void main(final String[] args) throws Exception { DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); @@ -102,9 +137,13 @@ public static void main(final String[] args) throws Exception { Map map = new HashMap(); map.put("html", "

My simple bold text in red!

"); map.put("numbers", Arrays.asList(new Number(100), new Number(-100), new Number(10))); + map.put("background", Color.ORANGE); try (FileOutputStream fos = new FileOutputStream(tmp); - TemplateDocument tpl = Configuration.builder().include(new HtmlToOoxml(dBuilder)).build().open(templateStream, "xlsx", fos)) { + TemplateDocument tpl = Configuration.builder() + .include(new HtmlToOoxml(dBuilder)) + .include(new ColorToXML(dBuilder)) + .build().open(templateStream, "xlsx", fos)) { tpl.process(map); } java.awt.Desktop.getDesktop().open(tmp); diff --git a/Intermediate/HtmlToExcel/template/Document.xlsx b/Intermediate/HtmlToExcel/template/Document.xlsx index b171a218adcab3ea0a8746784567e4bacd0eca70..85f6be4c7df274a1f7d1fcef6836446a47dc0996 100644 GIT binary patch delta 7438 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 F{{xtLe^dYf delta 6141 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 diff --git a/Intermediate/HtmlToWord/Readme.md b/Intermediate/HtmlToWord/Readme.md index a873e33f..0cf05222 100644 --- a/Intermediate/HtmlToWord/Readme.md +++ b/Intermediate/HtmlToWord/Readme.md @@ -20,6 +20,8 @@ This will leave old paragraph empty, which might lead to whitespace bloat. To de * replace-xml - which will insert XML at the place of the paragraph (instead of after it) * merge-xml - which will merge provided XML into the found structure +To ease the usage, instead of specifying this through tag metadata, they can be sent in XML via templater-xml attribute, e.g. templater-xml="merge-xml" + ### Document merging With version 6.1 Templater also supports document embedding in Word. This can be used for document merging, HTML/RTF import and similar purposes. To import embedded document, special type must be used: diff --git a/Intermediate/HtmlToWord/src/Program.cs b/Intermediate/HtmlToWord/src/Program.cs index 854c6556..bcd1d892 100644 --- a/Intermediate/HtmlToWord/src/Program.cs +++ b/Intermediate/HtmlToWord/src/Program.cs @@ -35,7 +35,10 @@ public static object ComplexHtmlConverter(object value, string metadata) { var paragraphs = Converter.Parse(value.ToString()); //return collection of XElement objects which will be inserted as is into document current tag - return paragraphs.Select(it => XElement.Parse(it.OuterXml)); + var xmls = paragraphs.Select(it => XElement.Parse(it.OuterXml)).ToList(); + //lets put special attribute directly on XML so we don't need to put it on tag + xmls[0].SetAttributeValue("templater-xml", "remove-old-xml"); + return xmls; } return value; } diff --git a/Intermediate/HtmlToWord/src/main/java/hr/ngs/templater/example/HtmlWordExample.java b/Intermediate/HtmlToWord/src/main/java/hr/ngs/templater/example/HtmlWordExample.java index 15aab4f3..562bc4ab 100644 --- a/Intermediate/HtmlToWord/src/main/java/hr/ngs/templater/example/HtmlWordExample.java +++ b/Intermediate/HtmlToWord/src/main/java/hr/ngs/templater/example/HtmlWordExample.java @@ -63,10 +63,12 @@ public ComplexHtmlConverter(DocumentBuilder dBuilder) { public Object format(Object value, String metadata) { if (metadata.equals("complex-html")) { NodeList bodyNodes = convert(value.toString(), dBuilder).getChildNodes(); - List elements = new ArrayList(bodyNodes.getLength()); + List elements = new ArrayList<>(bodyNodes.getLength()); for (int i = 0; i < bodyNodes.getLength(); i++) { elements.add((Element) bodyNodes.item(i)); } + //lets put special attribute directly on XML so we don't need to put it on tag + elements.get(0).setAttribute("templater-xml", "remove-old-xml"); return elements.toArray(new Element[0]); } return value; diff --git a/Intermediate/HtmlToWord/template/template.docx b/Intermediate/HtmlToWord/template/template.docx index f349943192fe2ec5955af70b8736aa88231f44b7..03029028818505ac0f76923467553b4be79c9e6b 100644 GIT binary patch delta 6310 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 format. Tags can have metadata which can be used for various customization purposes, such as formatting, verbalization and others. @@ -67,6 +67,7 @@ It can work for simple cases, such as single row in a table, but can be used for * [pushdown/pushright](Beginner/PushDownExample) - elements bellow/right of tags area will be moved according to builtin rules * [merge cells/named ranges/tables](Beginner/NamedRange) - influence push rules by extending the affected region * [XML binding](Advanced/XmlBinding) - custom XML will be changed/updated when bound in Word + * [streaming](Advanced/Streaming) - streaming over collections is also supported ## Examples diff --git a/RunAll/Program.cs b/RunAll/Program.cs index e9ea8ddd..7f895a5e 100644 --- a/RunAll/Program.cs +++ b/RunAll/Program.cs @@ -46,7 +46,7 @@ static void Main(string[] args) //TemplaterWeb // web app. run manually DoubleProcessing.Program.Main(args); SheetReport.Program.Main(args); - CsvStreaming.Program.Main(args); + Streaming.Program.Main(args); XmlBinding.Program.Main(args); DepartmentReport.Program.Main(args); //PowerQuery.Program.Main(args);//requires license file to work properly diff --git a/RunAll/RunAll.csproj b/RunAll/RunAll.csproj index e7e8eae6..6f6a7a45 100644 --- a/RunAll/RunAll.csproj +++ b/RunAll/RunAll.csproj @@ -52,10 +52,6 @@ - - {5BB2AABB-A28F-404F-8C37-DBE122E893F5} - CsvStreaming - {B7765E9A-952F-46C7-9E2A-4DB7A1A045F6} DepartmentReport @@ -72,6 +68,10 @@ {B3457322-FDDB-46AC-B445-2887C27BC723} SheetReport + + {5BB2AABB-A28F-404F-8C37-DBE122E893F5} + Streaming + {716992A2-B22C-463B-9D33-78CDDFB8973E} XmlBinding diff --git a/Templater.sln b/Templater.sln index df7a6431..34edc580 100644 --- a/Templater.sln +++ b/Templater.sln @@ -97,8 +97,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerQuery", "Advanced\Powe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MissingProperty", "Intermediate\MissingProperty\MissingProperty.csproj", "{AF4F0BF6-87C2-4A49-AFF0-CA8FBE5C53F2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsvStreaming", "Advanced\CsvStreaming\CsvStreaming.csproj", "{5BB2AABB-A28F-404F-8C37-DBE122E893F5}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePresentation", "Beginner\SimplePresentation\SimplePresentation.csproj", "{172BA03B-8863-4721-B22D-0DFD205CCF46}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedCharts", "Intermediate\SharedCharts\SharedCharts.csproj", "{25A57D11-D463-4293-817A-A967C2107CBF}" @@ -107,6 +105,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresentationTables", "Begin EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Paragraphs", "Beginner\Paragraphs\Paragraphs.csproj", "{25A57D63-D463-4090-817B-B967C2107CBF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Streaming", "Advanced\Streaming\Streaming.csproj", "{5BB2AABB-A28F-404F-8C37-DBE122E893F5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -567,16 +567,6 @@ Global {AF4F0BF6-87C2-4A49-AFF0-CA8FBE5C53F2}.Release|Mixed Platforms.Build.0 = Release|x86 {AF4F0BF6-87C2-4A49-AFF0-CA8FBE5C53F2}.Release|x86.ActiveCfg = Release|x86 {AF4F0BF6-87C2-4A49-AFF0-CA8FBE5C53F2}.Release|x86.Build.0 = Release|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|Any CPU.ActiveCfg = Debug|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|x86.ActiveCfg = Debug|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|x86.Build.0 = Debug|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|Any CPU.ActiveCfg = Release|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|Mixed Platforms.Build.0 = Release|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|x86.ActiveCfg = Release|x86 - {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|x86.Build.0 = Release|x86 {172BA03B-8863-4721-B22D-0DFD205CCF46}.Debug|Any CPU.ActiveCfg = Debug|x86 {172BA03B-8863-4721-B22D-0DFD205CCF46}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {172BA03B-8863-4721-B22D-0DFD205CCF46}.Debug|Mixed Platforms.Build.0 = Debug|x86 @@ -617,6 +607,16 @@ Global {25A57D63-D463-4090-817B-B967C2107CBF}.Release|Mixed Platforms.Build.0 = Release|x86 {25A57D63-D463-4090-817B-B967C2107CBF}.Release|x86.ActiveCfg = Release|x86 {25A57D63-D463-4090-817B-B967C2107CBF}.Release|x86.Build.0 = Release|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|Any CPU.ActiveCfg = Debug|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|x86.ActiveCfg = Debug|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Debug|x86.Build.0 = Debug|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|Any CPU.ActiveCfg = Release|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|Mixed Platforms.Build.0 = Release|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|x86.ActiveCfg = Release|x86 + {5BB2AABB-A28F-404F-8C37-DBE122E893F5}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/pom.xml b/pom.xml index 54d49135..a1d6e907 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ Intermediate/TemplaterJson Advanced/DoubleProcessing Advanced/SheetReport - Advanced/CsvStreaming + Advanced/Streaming Advanced/XmlBinding Advanced/PowerQuery Advanced/DepartmentReport @@ -271,7 +271,7 @@ hr.ngs.templater.example - csv-streaming-example + streaming-example ${project.version} diff --git a/src/test/java/DemoTests.java b/src/test/java/DemoTests.java index 57f64d84..3abfbea6 100644 --- a/src/test/java/DemoTests.java +++ b/src/test/java/DemoTests.java @@ -239,10 +239,9 @@ public void testSheets() throws Exception { SheetReportExample.main(null); } - @Test - public void testCsvStreaming() throws Exception { - CsvStreamingExample.main(null); + public void testStreaming() throws Exception { + StreamingExample.main(null); } @Test