From 60158163fce7401bb4a01ce510b933e5089d6f0c Mon Sep 17 00:00:00 2001 From: Manuel Ortiz Date: Tue, 21 Aug 2018 12:18:45 -0300 Subject: [PATCH 1/2] Add DocxTemplaterBulk. With this template engine, the library can process docx with many more scripts --- .../docx/DocxTemplaterBulk.java | 199 ++++++ .../org/scriptlet4docx/docx/Placeholder.java | 7 +- .../docx/DocxTemplaterBulkTest.java | 651 ++++++++++++++++++ .../docx/DocxTemplaterTest.java | 4 +- .../scriptlet4docx/docx/PlaceholderTest.java | 5 + .../resources/docx/DocxTemplaterTest-13.xml | 49 ++ 6 files changed, 912 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/scriptlet4docx/docx/DocxTemplaterBulk.java create mode 100644 src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java create mode 100644 src/test/resources/docx/DocxTemplaterTest-13.xml diff --git a/src/main/java/org/scriptlet4docx/docx/DocxTemplaterBulk.java b/src/main/java/org/scriptlet4docx/docx/DocxTemplaterBulk.java new file mode 100644 index 0000000..3aaf553 --- /dev/null +++ b/src/main/java/org/scriptlet4docx/docx/DocxTemplaterBulk.java @@ -0,0 +1,199 @@ +package org.scriptlet4docx.docx; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.codehaus.groovy.control.CompilationFailedException; +import org.scriptlet4docx.docx.Placeholder.PlaceholderType; +import org.scriptlet4docx.docx.Placeholder.ScriptWraps; +import org.scriptlet4docx.util.string.StringUtil; +import org.scriptlet4docx.util.xml.XMLUtils; + +/** + * + * Extend DocxTemplate to give you the ability to process .docx with many more scripts. + * Eliminating the problem of "Method code too large!" Do not confuse with + * {@link groovy.text.StreamingTemplateEngine} + * + *

Use:

Each N number of scriptlets in your .docx insert a mark "<!=BREAK!>" + * This will tell the engine that it can execute the scripts up to that point. + * Then, it will continue processing until the next <!=BREAK!> + * + * @author ortizman@gmail.com + * @author manuel.ortiz@fluxit.com.ar + * + */ +public class DocxTemplaterBulk extends DocxTemplater { + + final static String UTIL_FUNC_HOLDER = "__docxTemplaterInstance"; + final static String NULL_REPLACER_REF = UTIL_FUNC_HOLDER + ".replaceIfNull"; + private static String NEW_LINE_PLACEHOLDER = "26f679ad-e7fd-4d42-9e05-946f393c277d"; + + @SuppressWarnings("unused") + private String replaceIfNull(Object o) { + return o == null ? nullReplacement : String.valueOf(o); + } + + /** + * + * @param nullReplacement + * When scriptlet output is null this value take its place.
+ * Useful when you want nothing to be printed, or custom value + * like "UNKNOWN". + */ + public void setNullReplacement(String nullReplacement) { + this.nullReplacement = nullReplacement; + } + + private String nullReplacement = ""; + + private static Pattern scriptPattern = Pattern.compile( + "((<%=?(.*?)%>)|\\$\\{(.*?)\\}|(<\\!=?(.*?)\\!>))", Pattern.DOTALL | Pattern.MULTILINE); + + public DocxTemplaterBulk(File pathToDocx) { + super(pathToDocx); + } + + public DocxTemplaterBulk(InputStream inputStream, String templateKey) { + super(inputStream, templateKey); + } + + @Override + protected String processCleanedTemplate(String template, Map params) + throws CompilationFailedException, ClassNotFoundException, IOException { + + params = processParams(params); + + String replacement = UUID.randomUUID().toString(); + + List scripts = new ArrayList(); + + Matcher m = scriptPattern.matcher(template); + + while (m.find()) { + String scriptText = m.group(0); + Placeholder ph = new Placeholder(UUID.randomUUID().toString(), scriptText, PlaceholderType.SCRIPT); + + if (ph.scriptWrap == ScriptWraps.DOLLAR_PRINT) { + ph.setScriptTextNoWrap(m.group(4)); + } else if (ph.scriptWrap == ScriptWraps.SCRIPLET || ph.scriptWrap == ScriptWraps.SCRIPLET_PRINT) { + ph.setScriptTextNoWrap(m.group(3)); + } else if (ph.scriptWrap == ScriptWraps.BREAK) { + ph.setScriptTextNoWrap(""); + } + + scripts.add(ph); + } + + String replacedScriptsTemplate = m.replaceAll(replacement); + + List pieces = Arrays + .asList(StringUtils.splitByWholeSeparatorPreserveAllTokens(replacedScriptsTemplate, replacement)); + + if (pieces.size() != scripts.size() + 1) { + throw new IllegalStateException( + String.format( + "Programming bug was detected. Text pieces size does not match scripts size (%s, %s)." + + " Please report this as a bug to the library author.", + pieces.size(), scripts.size())); + } + + List tplSkeleton = new ArrayList(); + + int i = 0; + for (String piece : pieces) { + tplSkeleton.add(new Placeholder(UUID.randomUUID().toString(), piece, PlaceholderType.TEXT)); + + if (i < scripts.size()) { + tplSkeleton.add(scripts.get(i)); + } + i++; + } + + StringBuilder builder = new StringBuilder(); + StringBuilder partialResult = new StringBuilder(); + + for (Placeholder placeholder : tplSkeleton) { + if (PlaceholderType.SCRIPT == placeholder.type) { + + String cleanScriptNoWrap = XMLUtils.getNoTagsTrimText(placeholder.getScriptTextNoWrap()); + cleanScriptNoWrap = StringUtils.replaceEach(cleanScriptNoWrap, + new String[] { "&", ">", "<", """, "«", "»", "“", "”", "‘", "’" }, + new String[] { "&", ">", "<", "\"", "\"", "\"", "\"", "\"", "\"", "\"" }); + + cleanScriptNoWrap = cleanScriptNoWrap.trim(); + // Replacing missing replacements, at least on top level + if (cleanScriptNoWrap.matches("\\w+")) { + if (!params.containsKey(cleanScriptNoWrap)) { + params.put(cleanScriptNoWrap, null); + } + } + + if (placeholder.scriptWrap == ScriptWraps.DOLLAR_PRINT + || placeholder.scriptWrap == ScriptWraps.SCRIPLET_PRINT + || placeholder.scriptWrap == ScriptWraps.BREAK) { + cleanScriptNoWrap = NULL_REPLACER_REF + "(" + cleanScriptNoWrap + ")"; + } + String script = placeholder.constructWithCurrentScriptWrap(cleanScriptNoWrap); + builder.append(script); + + if (placeholder.scriptWrap == ScriptWraps.BREAK) { + partialResult.append(processPartialTemplate(builder.toString(), params)); + builder.setLength(0); // reset builder + } + + } else { + builder.append(placeholder.ph); + } + } + + String scriptAppliedStr; + + if (builder != null && builder.length() > 0) { + partialResult.append(processPartialTemplate(builder.toString(), params)); + } + scriptAppliedStr = partialResult.toString(); + + scriptAppliedStr = StringUtil.escapeSimpleSet(scriptAppliedStr); + + scriptAppliedStr = StringUtils.replace(scriptAppliedStr, NEW_LINE_PLACEHOLDER, ""); + + String result = scriptAppliedStr; + for (Placeholder placeholder : tplSkeleton) { + if (PlaceholderType.TEXT == placeholder.type) { + result = StringUtils.replace(result, placeholder.ph, placeholder.text); + } + } + + return result; + } + + private String processPartialTemplate(String template, Map params) { + final String methodName = "processPartialTemplate"; + + params.put(UTIL_FUNC_HOLDER, this); + + if (logger.isLoggable(Level.FINEST)) { + logger.logp(Level.FINEST, CLASS_NAME, methodName, String.format("\ntemplate = \n%s\n", template)); + } + + try { + return String.valueOf(getTemplateEngine().createTemplate(template).make(params)); + } catch (Throwable e) { + logger.logp(Level.SEVERE, CLASS_NAME, methodName, String.format("Cannot process template: [%s].", template), + e); + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/org/scriptlet4docx/docx/Placeholder.java b/src/main/java/org/scriptlet4docx/docx/Placeholder.java index ea9ee65..c748f63 100644 --- a/src/main/java/org/scriptlet4docx/docx/Placeholder.java +++ b/src/main/java/org/scriptlet4docx/docx/Placeholder.java @@ -20,7 +20,7 @@ public String constructWithCurrentScriptWrap(String scriptTextNoWrap) { format = "${%s}"; } else if (scriptWrap == ScriptWraps.SCRIPLET) { format = "<%%%s%%>"; - } else if (scriptWrap == ScriptWraps.SCRIPLET_PRINT) { + } else if (scriptWrap == ScriptWraps.SCRIPLET_PRINT || scriptWrap == ScriptWraps.BREAK) { format = "<%%=%s%%>"; } else { throw new RuntimeException(String.format("ScriptWrap is undefined: %s", scriptWrap)); @@ -47,6 +47,8 @@ private void detectScriptWrap() { scriptWrap = ScriptWraps.SCRIPLET_PRINT; } else if (noSpaces.startsWith("<%")) { scriptWrap = ScriptWraps.SCRIPLET; + } else if(noSpaces.startsWith("<!")) { + scriptWrap = ScriptWraps.BREAK; } else { throw new IllegalArgumentException(String.format("Script wrap cannot be detected: [%s]", text)); } @@ -55,7 +57,8 @@ private void detectScriptWrap() { static enum ScriptWraps { DOLLAR_PRINT, // ${} SCRIPLET, // <%%> - SCRIPLET_PRINT // <%=%> + SCRIPLET_PRINT, // <%=%> + BREAK // } static enum PlaceholderType { diff --git a/src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java new file mode 100644 index 0000000..133466d --- /dev/null +++ b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java @@ -0,0 +1,651 @@ +package org.scriptlet4docx.docx; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.scriptlet4docx.docx.TemplateContent.ContentItem; +import org.scriptlet4docx.util.test.TestUtils; + +import mockit.Expectations; +import mockit.Mocked; +import mockit.NonStrictExpectations; +import mockit.Verifications; + +public class DocxTemplaterBulkTest extends Assert { + + static HashMap params; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + params = new HashMap(); + HashMap contract = new HashMap(); + contract.put("number", "123#445"); + params.put("value", 1); + params.put("contract", contract); + params.put("escapeTest", "This should be escaped: &, <, >."); + + List personList = new ArrayList(); + personList.add("vasya"); + personList.add("petya"); + + params.put("personList", personList); + params.put("menList", personList); + + List> employeeList = new ArrayList>(); + HashMap p1 = new HashMap(); + p1.put("name", "Tom"); + p1.put("address", "Moscow"); + HashMap p2 = new HashMap(); + p2.put("name", "John"); + p2.put("address", "New York"); + employeeList.add(p1); + employeeList.add(p2); + + params.put("employeeList", employeeList); + HashMap p3 = new HashMap(); + p3.put("nomeCliente", "Bob Smith"); + + params.put("crm", p3); + + } + + @Test + public void testProcessScriptedTemplateBulk() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-13.xml"); + + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(result.contains("123#445")); + assertTrue(StringUtils.countMatches(result, "123#445") == 4); + assertFalse(result.contains("BREAK")); + } + + @Test + public void testProcessScriptedTemplate() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-1.xml"); + + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(result.contains("123#445")); + assertTrue(StringUtils.countMatches(result, "123#445") == 4); + } + + + @Test + public void testProcessScriptedTemplate_brokenType1() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-2.xml"); + + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(result.contains("123#445")); + assertTrue(StringUtils.countMatches(result, "123#445") == 1); + } + + @Test + public void testPreProcessTableScripting_multiTr() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-5.xml"); + + String result = TableScriptingProcessor.process(template); + + assertTrue(result != null); + assertTrue(!result.contains("$[")); + + assertTrue(result.contains("<% def iterStatus=0; for ( wawawa in tour.wawawa ) { iterStatus++; %>")); + assertTrue(result.contains("${wawawa.myway}")); + + assertTrue(result.contains("<% iterStatus=0; for ( mamama in tour1.mamama ) { iterStatus++; %>")); + assertTrue(result.contains("${mamama.myway}")); + + } + + @Test + public void testProcessScriptedTemplate_tableScripting() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-6.xml"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(!result.contains("$[")); + + assertTrue(result.contains(">vasya<")); + assertTrue(result.contains(">petya<")); + + } + + @Test + public void testProcessScriptedTemplate_brokenType2() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-3.xml"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(result.contains("123#445")); + assertTrue(StringUtils.countMatches(result, "123#445") == 1); + } + + @Test + public void testProcessScriptedTemplate_brokenType2_noProcess() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-7.xml"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(template.equals(result)); + } + + @Test + public void testProcessScriptedTemplate_tableScripting_iterStatus() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-8.xml"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(!result.contains("$[")); + + assertTrue(result.contains(">vasya<")); + assertTrue(result.contains(">petya<")); + + assertTrue(!result.contains(">${iterStatus}<")); + + assertTrue(result.contains(">1<")); + assertTrue(result.contains(">2<")); + + } + + @Test + public void testProcessScriptedTemplate_tableScripting_iterStatus_multiTable() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-4.xml"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(!result.contains("$[")); + + assertTrue(result.contains(">vasya<")); + assertTrue(result.contains(">petya<")); + + assertTrue(!result.contains(">${iterStatus}<")); + + assertTrue(result.contains(">1<")); + assertTrue(result.contains(">2<")); + + assertTrue(StringUtils.countMatches(result, ">1<") == 2); + assertTrue(StringUtils.countMatches(result, ">2<") == 2); + + } + + @Test + public void testProcessScriptedTemplate_logicScriptlets() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-9.xml"); + + HashMap params1 = new HashMap(); + params1.put("value", 1); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like kitties")); + + params1 = new HashMap(); + params1.put("value", 0); + + template = templater.cleanupTemplate(template); + result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like dogs")); + } + + @Test + public void testProcessScriptedTemplate_logicScriptlets_gt() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-15.xml"); + + HashMap params1 = new HashMap(); + params1.put("value", 1); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like dogs")); + + params1 = new HashMap(); + params1.put("value", 2); + + template = templater.cleanupTemplate(template); + result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like kitties")); + } + + @Test + public void testProcessScriptedTemplate_logicScriptlets_lt() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-16.xml"); + + HashMap params1 = new HashMap(); + params1.put("value", 1); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like kitties")); + + params1 = new HashMap(); + params1.put("value", 2); + + template = templater.cleanupTemplate(template); + result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like dogs")); + } + + @Test + public void testProcessScriptedTemplate_logicScriptlets_quote() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-17.xml"); + + HashMap params1 = new HashMap(); + params1.put("value", "kitties"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like kitties")); + + params1 = new HashMap(); + params1.put("value", "dogs"); + + template = templater.cleanupTemplate(template); + result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like dogs")); + } + + @Test + public void testProcessScriptedTemplate_logicScriptlets_quoteCurly() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-18.xml"); + + HashMap params1 = new HashMap(); + params1.put("value", "kitties"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like kitties")); + + params1 = new HashMap(); + params1.put("value", "dogs"); + + template = templater.cleanupTemplate(template); + result = templater.processCleanedTemplate(template, params1); + + assertTrue(result != null); + assertTrue(!result.contains("else")); + assertTrue(!result.contains("if")); + + assertTrue(result.contains("mom and dad")); + assertTrue(result.contains("like dogs")); + } + + @Test + public void testProcessScriptedTemplate_spacePreserve() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-10.xml"); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result != null); + assertTrue(!result.contains("print")); + + assertTrue(result.contains("like dogs")); + } + + @Test + public void testProcess_file() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-1.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-1-file-result-1.docx"); + resFile.delete(); + + DocxTemplaterBulk docxTemplater = new DocxTemplaterBulk(inFile); + + docxTemplater.process(resFile, params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + @Test + public void testProcess_file2() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-13.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-1-file-result-2.docx"); + resFile.delete(); + + DocxTemplaterBulk docxTemplater = new DocxTemplaterBulk(inFile); + + docxTemplater.process(resFile, params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + @Test + public void testProcess_file3() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-14.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-1-file-result-3.docx"); + resFile.delete(); + + DocxTemplaterBulk docxTemplater = new DocxTemplaterBulk(inFile); + + docxTemplater.process(resFile, params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + @Test + public void testProcess_withInputStreamAsOutput() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-1.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-stream-2-result.docx"); + resFile.delete(); + + DocxTemplaterBulk templater = new DocxTemplaterBulk(inFile); + + InputStream resStream = templater.processAndReturnInputStream(params); + + FileUtils.copyInputStreamToFile(resStream, resFile); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + + assertTrue(new File(TemplateFileManager.getInstance().getTemplatesDir(), + TemplateFileManager.DOC_READY_STREAM_FOLDER_NAME).listFiles().length == 0); + } + + @Test + public void testProcess_withOutputStream() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-1.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-stream-3-result.docx"); + resFile.delete(); + + DocxTemplaterBulk docxTemplater = new DocxTemplaterBulk(inFile); + + docxTemplater.process(new FileOutputStream(resFile), params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + @Test + public void testProcess_fileMultiRun(final @Mocked TemplateFileManager mgr) throws Exception { + File inFile = new File("1"); + final File resFile = new File("2"); + + final DocxTemplaterBulk docxTemplater1 = new DocxTemplaterBulk(inFile); + + new NonStrictExpectations() { + { + TemplateFileManager.getInstance(); + result = mgr; + } + }; + + final TemplateContent c1 = new TemplateContent(Arrays.asList(new ContentItem("", ""))); + final TemplateContent c2 = new TemplateContent(Arrays.asList(new ContentItem("", ""))); + final TemplateContent c3 = new TemplateContent(Arrays.asList(new ContentItem("", ""))); + + new Expectations(docxTemplater1) { + { + docxTemplater1.setupTemplate(); + result = "t1"; + + mgr.getTemplateContent("t1"); + result = c1; + + mgr.isPreProcessedTemplateExists("t1"); + result = false; + + docxTemplater1.cleanupTemplate(c1); + result = c2; + + mgr.savePreProcessed("t1", c2); + + docxTemplater1.processCleanedTemplate(c2, params); + result = c3; + + docxTemplater1.processResult(resFile, "t1", c3); + } + }; + + docxTemplater1.process(resFile, params); + } + + @Test + public void testProcess_stream() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-1.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-1-stream-result.docx"); + resFile.delete(); + + DocxTemplaterBulk docxTemplater = new DocxTemplaterBulk(new FileInputStream(inFile), "k1"); + + docxTemplater.process(resFile, params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + @Test + public void testProcess_header() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-2-header.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-2-header.docx"); + resFile.delete(); + + DocxTemplaterBulk docxTemplater = new DocxTemplaterBulk(new FileInputStream(inFile), "k1"); + + docxTemplater.process(resFile, params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + @Test + public void testProcess_streamMultiRun() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-1.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-1-stream-result1.docx"); + resFile.delete(); + + final InputStream stream1 = new FileInputStream(inFile); + final InputStream stream2 = new FileInputStream(inFile); + + final DocxTemplaterBulk docxTemplater1 = new DocxTemplaterBulk(stream1, "k2"); + final DocxTemplaterBulk docxTemplater2 = new DocxTemplaterBulk(stream2, "k2"); + + new NonStrictExpectations(stream2) { + }; + + docxTemplater1.process(resFile, params); + docxTemplater2.process(resFile, params); + + // testing that stream2 was not actuall read but was closed + new Verifications() { + { + stream2.read((byte[]) any); + times = 0; + } + }; + new Verifications() { + { + stream2.read((byte[]) any, anyInt, anyInt); + times = 0; + } + }; + new Verifications() { + { + stream2.close(); + } + }; + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + private File none; + + @Test + public void testProcessScriptedTemplate_escapeAmpLtGt() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-11.xml"); + + HashMap params = new HashMap(); + + params.put("escapeTest", "This should be escaped: &, <, >."); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertTrue(result.contains(">This should be escaped: &, <, >.<")); + } + + @Test + public void testProcessScriptedTemplate_nullsReplacement() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-12.xml"); + + HashMap params = new HashMap(); + + params.put("someNullyVar", null); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertFalse(result.contains("space=\"preserve\">null<")); + assertTrue(result.contains("space=\"preserve\"><")); + + assertFalse(result.contains("space=\"arg1\">null<")); + assertTrue(result.contains("space=\"arg1\"><")); + + templater.setNullReplacement("UNKNOWD"); + result = templater.processCleanedTemplate(template, params); + + assertTrue(result.contains("space=\"preserve\">UNKNOWD<")); + assertTrue(result.contains("space=\"arg1\">UNKNOWD<")); + } + + @Test + public void testProcessScriptedTemplate_noSuchPropertyNullsReplacement() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-12.xml"); + + HashMap params = new HashMap(); + + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + + assertFalse(result.contains("space=\"preserve\">null<")); + assertTrue(result.contains("space=\"preserve\"><")); + + assertFalse(result.contains("space=\"arg1\">null<")); + assertTrue(result.contains("space=\"arg1\"><")); + + templater.setNullReplacement("UNKNOWD"); + result = templater.processCleanedTemplate(template, params); + + assertTrue(result.contains("space=\"preserve\">UNKNOWD<")); + assertTrue(result.contains("space=\"arg1\">UNKNOWD<")); + } + + @Test + public void testProcessScriptedTemplate_booleanAndCond() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-19.xml"); + + HashMap params = new HashMap(); + + params.put("cond1", "1"); + params.put("cond2", true); + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + String result = templater.processCleanedTemplate(template, params); + assertTrue(result.contains("like kitties")); + + params.put("cond1", false); + result = templater.processCleanedTemplate(template, params); + assertFalse(result.contains("like kitties")); + } + + @Test + public void testProcessScriptedTemplate_newLine() throws Exception { + String template = TestUtils.readResource("/docx/DocxTemplaterTest-20.xml"); + + DocxTemplaterBulk templater = new DocxTemplaterBulk(none); + template = templater.cleanupTemplate(template); + params.put("hasNewLines", "this is A\n this is B\r\n this is C"); + String result = templater.processCleanedTemplate(template, DocxTemplater.processParams(params)); + + assertTrue(result != null); + assertTrue(result.contains("this is A")); + assertTrue(StringUtils.countMatches(result, "this is A") == 4); + } +} diff --git a/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java index 280284d..2214f82 100644 --- a/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java +++ b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java @@ -62,7 +62,7 @@ public static void setUpBeforeClass() throws Exception { @Test public void testProcessScriptedTemplate() throws Exception { - String template = TestUtils.readResource("/docx/DocxTemplaterTest-1.xml"); + String template = TestUtils.readResource("/docx/DocxTemplaterTest-13.xml"); DocxTemplater templater = new DocxTemplater(none); template = templater.cleanupTemplate(template); @@ -71,7 +71,9 @@ public void testProcessScriptedTemplate() throws Exception { assertTrue(result != null); assertTrue(result.contains("123#445")); assertTrue(StringUtils.countMatches(result, "123#445") == 4); + } + @Test public void testProcessScriptedTemplate_brokenType1() throws Exception { diff --git a/src/test/java/org/scriptlet4docx/docx/PlaceholderTest.java b/src/test/java/org/scriptlet4docx/docx/PlaceholderTest.java index b9dd732..2c2db64 100644 --- a/src/test/java/org/scriptlet4docx/docx/PlaceholderTest.java +++ b/src/test/java/org/scriptlet4docx/docx/PlaceholderTest.java @@ -25,6 +25,11 @@ public void testConstructWithCurrentScriptWrap() { "<% foo.bar() %>", PlaceholderType.SCRIPT); assertEquals("<% foo.bar() %>", ph.constructWithCurrentScriptWrap(" foo.bar() ")); + + ph = new Placeholder(UUID.randomUUID().toString(), + "<!=BREAK !>", PlaceholderType.SCRIPT); + + assertEquals("<%= BREAK %>", ph.constructWithCurrentScriptWrap(" BREAK ")); } } diff --git a/src/test/resources/docx/DocxTemplaterTest-13.xml b/src/test/resources/docx/DocxTemplaterTest-13.xml new file mode 100644 index 0000000..ece4e74 --- /dev/null +++ b/src/test/resources/docx/DocxTemplaterTest-13.xml @@ -0,0 +1,49 @@ + + + + + + + + ${contract.number} > <!=BREAK!> + + + + + + + + + ${ + contract.number + } + + + + + + + + + <%= contract.number %> <!=BREAK!> + + + + + + + + + <% contract.number %> + + + + + + + + + <%= + contract.number + %> + \ No newline at end of file From 54c689f7168951221e85f5bda098101f3d9616d4 Mon Sep 17 00:00:00 2001 From: Manuel Ortiz Date: Mon, 4 Mar 2019 17:09:05 -0300 Subject: [PATCH 2/2] Add test that reproduces " Method code too large! " DocxTemplateBulk success test DocxTemplate fail test (expected runtimeexception) --- .../docx/DocxTemplaterBulkTest.java | 14 ++++++++++++++ .../scriptlet4docx/docx/DocxTemplaterTest.java | 17 +++++++++++++++++ .../docx/DocxTemplaterTest-large.docx | Bin 0 -> 12231 bytes 3 files changed, 31 insertions(+) create mode 100644 src/test/resources/docx/DocxTemplaterTest-large.docx diff --git a/src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java index 133466d..826ba84 100644 --- a/src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java +++ b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterBulkTest.java @@ -408,6 +408,20 @@ public void testProcess_file3() throws Exception { assertTrue(resFile.length() > 0); } + @Test + public void testProcess_file4() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-large.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-1-file-result-large.docx"); + resFile.delete(); + + DocxTemplaterBulk docxTemplater = new DocxTemplaterBulk(inFile); + + docxTemplater.process(resFile, params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + @Test public void testProcess_withInputStreamAsOutput() throws Exception { File inFile = new File("src/test/resources/docx/DocxTemplaterTest-1.docx"); diff --git a/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java index 2214f82..feabc5b 100644 --- a/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java +++ b/src/test/java/org/scriptlet4docx/docx/DocxTemplaterTest.java @@ -22,6 +22,8 @@ import org.scriptlet4docx.docx.TemplateContent.ContentItem; import org.scriptlet4docx.util.test.TestUtils; +import groovy.lang.GroovyRuntimeException; + public class DocxTemplaterTest extends Assert { static HashMap params; @@ -395,6 +397,21 @@ public void testProcess_file3() throws Exception { assertTrue(resFile.length() > 0); } + @Test(expected = RuntimeException.class) + public void testProcess_file4() throws Exception { + File inFile = new File("src/test/resources/docx/DocxTemplaterTest-large.docx"); + File resFile = new File("target/test-files/DocxTemplaterTest-1-file-result-large.docx"); + resFile.delete(); + + DocxTemplater docxTemplater = new DocxTemplater(inFile); + + docxTemplater.process(resFile, params); + + assertTrue(resFile.exists()); + assertTrue(resFile.length() > 0); + } + + @Test public void testProcess_withInputStreamAsOutput() throws Exception { File inFile = new File("src/test/resources/docx/DocxTemplaterTest-1.docx"); diff --git a/src/test/resources/docx/DocxTemplaterTest-large.docx b/src/test/resources/docx/DocxTemplaterTest-large.docx new file mode 100644 index 0000000000000000000000000000000000000000..1a7acc57c48935e6e99b35978118bd57bbe08c1c GIT binary patch literal 12231 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