/******************************************************************************* * 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.ofbiz.base.util.string.test; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import junit.framework.TestCase; import org.apache.ofbiz.base.lang.SourceMonitored; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.string.FlexibleStringExpander; import org.apache.ofbiz.base.conversion.AbstractConverter; import org.apache.ofbiz.base.conversion.ConversionException; import org.apache.ofbiz.base.conversion.Converters; @SourceMonitored public class FlexibleStringExpanderTests extends TestCase { private static final Locale localeToTest = new Locale("en", "US"); private static final Locale badLocale = new Locale("fr"); private static final TimeZone timeZoneToTest = TimeZone.getTimeZone("PST"); private static final TimeZone badTimeZone = TimeZone.getTimeZone("GMT"); private boolean wasVerbose; public FlexibleStringExpanderTests(String name) { super(name); } private static void parserTest(String label, String input, boolean checkCache, String toString) { FlexibleStringExpander fse = FlexibleStringExpander.getInstance(input, false); assertEquals(label + ":toString(no-cache)", toString, fse.toString()); fse = FlexibleStringExpander.getInstance(input, true); assertEquals(label + ":toString(cache)", toString, fse.toString()); if (checkCache) { assertEquals(label + ":same-cache", fse, FlexibleStringExpander.getInstance(input, true)); } } public void testParsing() { parserTest("visible nested replacement", "${'Hello ${var}'}!", true, "${'Hello ${var}'}!"); parserTest("hidden (runtime) nested null callreplacement", "Hello ${${groovy:" + FlexibleStringExpanderTests.class.getName() + ".StaticReturnNull()}}World!", true, "Hello ${${groovy:" + FlexibleStringExpanderTests.class.getName() + ".StaticReturnNull()}}World!"); parserTest("UEL integration(nested): throw Exception", "${${throwException.value}}", true, "${${throwException.value}}"); parserTest("nested-constant-emptynest-emptynest", "${a${}${}", true, "${a${}${}"); parserTest("null", null, true, ""); parserTest("empty", "", true, ""); parserTest("constant-only", "a", false, "a"); parserTest("nested-constant-emptynest-emptynest", "${a${}${}", true, "${a${}${}"); parserTest("groovy", "${groovy:}", true, "${groovy:}"); parserTest("escaped", "\\${}", true, "\\${}"); parserTest("constant-escaped", "a\\${}", true, "a\\${}"); parserTest("escaped-groovy", "\\${groovy:}", true, "\\${groovy:}"); parserTest("missing-}", "${", true, "${"); parserTest("nested-constant-missing-}", "${a${}", true, "${a${}"); parserTest("nested-constant-nested-nested-missing-}", "${a${${}", true, "${a${${}"); parserTest("escaped-missing-}", "\\${", true, "\\${"); parserTest("constant-escaped-missing-}", "a\\${", true, "a\\${"); parserTest("currency", "${?currency(", true, "${?currency("); parserTest("currency", "${?currency()", true, "${?currency()"); parserTest("currency", "${price?currency(", true, "${price?currency("); parserTest("currency", "${price?currency()", true, "${price?currency()"); parserTest("currency", "${?currency(usd", true, "${?currency(usd"); parserTest("currency", "${?currency(usd)", true, "${?currency(usd)"); parserTest("currency", "${price?currency(usd", true, "${price?currency(usd"); parserTest("currency", "${price?currency(usd)", true, "${price?currency(usd)"); parserTest("currency", "${?currency(}", true, "${?currency(}"); parserTest("currency", "${?currency()}", true, "${?currency()}"); parserTest("currency", "${?currency(usd}", true, "${?currency(usd}"); parserTest("currency", "${?currency(usd)}", true, "${?currency(usd)}"); parserTest("currency", "${price?currency(}", true, "${price?currency(}"); parserTest("currency", "${price?currency()}", true, "${price?currency()}"); parserTest("currency", "${price?currency(usd}", true, "${price?currency(usd}"); parserTest("currency", "${price?currency(usd)}", true, "${price?currency(usd)}"); parserTest("currency", "a${price?currency(usd)}b", true, "a${price?currency(usd)}b"); } private static void fseTest(String label, String input, Map<String, Object> context, String compare, boolean isEmpty) { fseTest(label, input, context, null, null, compare, isEmpty); } private static void doFseTest(String label, String input, FlexibleStringExpander fse, Map<String, Object> context, TimeZone timeZone, Locale locale, String compare, Object expand, boolean isEmpty) { assertEquals("isEmpty:" + label, isEmpty, fse.isEmpty()); if (input == null) { assertEquals("getOriginal():" + label, "", fse.getOriginal()); assertEquals("toString():" + label, "", fse.toString()); assertEquals("expandString(null):" + label, "", fse.expandString(null)); assertEquals("expand(null):" + label, null, fse.expand(null)); if (timeZone == null) { assertEquals("expandString(null):" + label, "", fse.expandString(null, locale)); assertEquals("expand(null):" + label, null, fse.expand(null, locale)); } else { assertEquals("expandString(null):" + label, "", fse.expandString(null, timeZone, locale)); assertEquals("expand(null):" + label, null, fse.expand(null, timeZone, locale)); } } else { assertEquals("getOriginal():" + label, input, fse.getOriginal()); assertEquals("toString():" + label, input, fse.toString()); assertEquals("expandString(null):" + label, input, fse.expandString(null)); assertEquals("expand(null):" + label, null, fse.expand(null)); if (timeZone == null) { assertEquals("expandString(null):" + label, input, fse.expandString(null, locale)); assertEquals("expand(null):" + label, null, fse.expand(null, locale)); } else { assertEquals("expandString(null):" + label, input, fse.expandString(null, timeZone, locale)); assertEquals("expand(null):" + label, null, fse.expand(null, timeZone, locale)); } } if (locale == null) { assertEquals(label, compare, fse.expandString(context)); assertEquals("expand:" + label, expand, fse.expand(context)); } else { Locale defaultLocale = Locale.getDefault(); TimeZone defaultTimeZone = TimeZone.getDefault(); try { Locale.setDefault(locale); TimeZone.setDefault(timeZone); assertEquals(label, compare, fse.expandString(context, null, null)); assertEquals(label, expand, fse.expand(context, null, null)); Locale.setDefault(badLocale); TimeZone.setDefault(badTimeZone); assertNotSame(label, compare, fse.expandString(context, null, null)); if (input != null) { assertNotSame(label, expand, fse.expand(context, null, null)); } Map<String, Object> autoUserLogin = new HashMap<String, Object>(); autoUserLogin.put("lastLocale", locale.toString()); autoUserLogin.put("lastTimeZone", timeZone == null ? null : timeZone.getID()); context.put("autoUserLogin", autoUserLogin); assertEquals(label, compare, fse.expandString(context, null, null)); assertEquals(label, expand, fse.expand(context, null, null)); autoUserLogin.put("lastLocale", badLocale.toString()); autoUserLogin.put("lastTimeZone", badTimeZone.getID()); assertNotSame(label, compare, fse.expandString(context, null, null)); if (input != null) { assertNotSame(label, expand, fse.expand(context, null, null)); } context.remove("autoUserLogin"); context.put("locale", locale); context.put("timeZone", timeZone); assertEquals(label, compare, fse.expandString(context, null, null)); assertEquals(label, expand, fse.expand(context, null, null)); context.put("locale", badLocale); context.put("timeZone", badTimeZone); assertNotSame(label, compare, fse.expandString(context, null, null)); if (input != null) { assertNotSame(label, expand, fse.expand(context, null, null)); } context.remove("locale"); context.remove("timeZone"); assertEquals(label, compare, fse.expandString(context, timeZone, locale)); assertEquals(label, expand, fse.expand(context, timeZone, locale)); assertNotSame(label, compare, fse.expandString(context, badTimeZone, badLocale)); if (input != null) { assertNotSame(label, expand, fse.expand(context, badTimeZone, badLocale)); } } finally { Locale.setDefault(defaultLocale); TimeZone.setDefault(defaultTimeZone); } } } private static void fseTest(String label, String input, Map<String, Object> context, TimeZone timeZone, Locale locale, String compare, boolean isEmpty) { fseTest(label, input, context, timeZone, locale, compare, compare, isEmpty); } private static void fseTest(String label, String input, Map<String, Object> context, TimeZone timeZone, Locale locale, String compare, Object expand, boolean isEmpty) { FlexibleStringExpander fse = FlexibleStringExpander.getInstance(input); doFseTest(label, input, fse, context, timeZone, locale, compare, expand, isEmpty); assertEquals("static expandString:" + label, compare, FlexibleStringExpander.expandString(input, context, timeZone, locale)); if (input == null) { assertEquals("static expandString(null, null):" + label, "", FlexibleStringExpander.expandString(input, null)); assertEquals("static expandString(null, null):" + label, "", FlexibleStringExpander.expandString(input, null, locale)); } else { assertEquals("static expandString(input, null):" + label, input, FlexibleStringExpander.expandString(input, null)); assertEquals("static expandString(input, null):" + label, input, FlexibleStringExpander.expandString(input, null, locale)); } if (!fse.isEmpty()) { fse = FlexibleStringExpander.getInstance(input, false); doFseTest(label, input, fse, context, timeZone, locale, compare, expand, isEmpty); } } public static String StaticReturnNull() { return null; } @Override public void setUp() { wasVerbose = Debug.isOn(Debug.VERBOSE); if (getName().equals("testWithVerbosity")) { Debug.set(Debug.VERBOSE, true); } Converters.registerConverter(new SpecialNumberToString()); } @Override public void tearDown() { Debug.set(Debug.VERBOSE, wasVerbose); } public static class ThrowException { public Object getValue() throws Exception { throw new Exception(); } } public static class ThrowNPE { public Object getValue() { throw new NullPointerException(); } } public static class SpecialNumberToString extends AbstractConverter<SpecialNumber, String> { public SpecialNumberToString() { super(SpecialNumber.class, String.class); } public String convert(SpecialNumber obj) throws ConversionException { throw new NullPointerException(); } } @SuppressWarnings("serial") public static class SpecialNumber extends BigDecimal { public SpecialNumber(String value) { super(value); } @Override public String toString() { return getClass().getName(); } } public void testWithVerbosity() { everythingTest(); } public void testQuietly() { everythingTest(); } private void everythingTest() { Map<String, Object> testMap = new HashMap<String, Object>(); testMap.put("date", new java.util.Date(1234567890)); testMap.put("usd", "USD"); testMap.put("amount", new BigDecimal("1234567.89")); testMap.put("blank", ""); testMap.put("exc", "Exception"); testMap.put("nullVar", null); testMap.put("throwException", new ThrowException()); testMap.put("throwNPE", new ThrowNPE()); testMap.put("var", "World"); testMap.put("nested", "Hello ${var}"); testMap.put("testMap", testMap); testMap.put("nestedNull", "Hello ${nullVar}${var}"); testMap.put("specialNumber", new SpecialNumber("1.00")); List<String> testList = new ArrayList<String>(); testList.add("World"); testMap.put("testList", testList); fseTest("null FlexibleStringExpander, null map", null, null, null, null, "", null, true); fseTest("null FlexibleStringExpander", null, testMap, null, null, "", null, true); fseTest("null context", "Hello World!", null, null, null, "Hello World!", null, false); fseTest("plain string", "Hello World!", testMap, null, null, "Hello World!", "Hello World!", false); fseTest("simple replacement", "Hello ${var}!", testMap, "Hello World!", false); fseTest("null FlexibleStringExpander with timeZone/locale", null, testMap, timeZoneToTest, localeToTest, "", null, true); fseTest("empty FlexibleStringExpander", "", testMap, null, null, "", null, true); fseTest("UEL integration(nested): throw Exception", "${${throwException.value}}", testMap, "", false); fseTest("UEL integration: throw Exception", "${throwException.value}", testMap, null, null, "", null, false); fseTest("UEL integration(nested): throw Exception", "${throw${exc}.value}", testMap, "", false); fseTest("UEL integration(nested): throw NPE", "${throwNPE${blank}.value}", testMap, "", false); fseTest("visible nested replacement", "${'Hello ${var}'}!", testMap, "Hello World!", false); fseTest("blank nested replacement", "${'Hel${blank}lo ${var}'}!", testMap, "Hello World!", false); fseTest("UEL integration(nested): null", "${${nu${nullVar}ll}}", testMap, "", false); fseTest("UEL integration(nested): NPE", "${${nullVar.noProp}}", testMap, "", false); fseTest("UEL integration(nested): missing", "${${noL${nullVar}ist[0]}}", testMap, "", false); fseTest("date w/ timezone", "The date is ${date}.", testMap, timeZoneToTest, localeToTest, "The date is 1970-01-14 22:56:07.890.", "The date is 1970-01-14 22:56:07.890.", false); fseTest("just bad", "${foobar", testMap, "${foobar", false); fseTest("constant and bad", "Hello${foobar", testMap, "Hello${foobar", false); fseTest("good and bad", "Hello ${var}${foobar", testMap, "Hello World${foobar", false); fseTest("plain-currency(USD)", "${amount?currency(${usd})}", testMap, null, localeToTest, "$1,234,567.89", false); fseTest("currency(USD)", "The total is ${amount?currency(${usd})}.", testMap, null, localeToTest, "The total is $1,234,567.89.", false); fseTest("currency(USD): null", "The total is ${testMap.missing?currency(${usd})}.", testMap, null, localeToTest, "The total is .", false); fseTest("currency(USD): missing", "The total is ${noList[0]?currency(${usd})}.", testMap, null, localeToTest, "The total is .", false); fseTest("currency(USD): exception", "The total is ${throwException.value?currency(${usd})}.", testMap, null, localeToTest, "The total is .", false); fseTest("null nested", "${${nullVar}}!", testMap, "!", false); fseTest("groovy: script", "${groovy:return \"Hello \" + var + \"!\";}", testMap, "Hello World!", false); fseTest("groovy: null", "${groovy:return null;}!", testMap, "!", false); fseTest("groovy missing property", "${groovy: return noList[0]}", testMap, null, null, "", null, false); fseTest("groovy: throw Exception", "${groovy:return throwException.value;}!", testMap, "!", false); fseTest("groovy: converter exception", "${groovy:return specialNumber;}!", testMap, SpecialNumber.class.getName() + "!", false); fseTest("UEL integration: Map", "Hello ${testMap.var}!", testMap, "Hello World!", false); fseTest("UEL integration: blank", "Hello ${testMap.blank}World!", testMap, "Hello World!", false); fseTest("UEL integration: List", "Hello ${testList[0]}!", testMap, "Hello World!", false); fseTest("UEL integration: null", "${null}", testMap, null, null, "", null, false); fseTest("UEL integration: null dereference", "${nullVar.noProp}", testMap, null, null, "", null, false); fseTest("UEL integration: throw NPE", "${" + FlexibleStringExpanderTests.class.getName() + ".ThrowNPE.noProp}", testMap, null, null, "", null, false); fseTest("UEL integration: missing", "${noList[0]}", testMap, null, null, "", null, false); fseTest("Escaped expression", "This is an \\${escaped} expression", testMap, "This is an ${escaped} expression", false); fseTest("Escaped(groovy) expression", "This is an \\${groovy:escaped} expression", testMap, "This is an ${groovy:escaped} expression", false); fseTest("nested UEL integration(return BigDecimal)", "${a${'moun'}t}", testMap, null, null, "1,234,567.89", testMap.get("amount"), false); fseTest("UEL integration(return BigDecimal)", "${amount}", testMap, null, null, "1,234,567.89", testMap.get("amount"), false); fseTest("groovy: return BigDecimal", "${groovy: return amount;}", testMap, null, null, "1,234,567.89", testMap.get("amount"), false); } }