package test;

import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.jar.Manifest;

import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Builder;
import aQute.bnd.osgi.Jar;
import aQute.bnd.osgi.Macro;
import aQute.bnd.osgi.Processor;
import aQute.lib.io.IO;
import junit.framework.TestCase;

@SuppressWarnings("resource")
public class MacroTest extends TestCase {

	public void testFilterExpression() throws Exception {
		Processor p = new Processor();
		p.setProperty("a", "A");
		p.setProperty("b", "1");

		assertEquals("true", p.getReplacer().process("${if;(a=A)}"));
		assertEquals("true", p.getReplacer().process("${if;(a>=A)}"));
		assertEquals("true", p.getReplacer().process("${if;(a<=A)}"));
		assertEquals("", p.getReplacer().process("${if;(a<A)}"));
		assertEquals("", p.getReplacer().process("${if;(a>A)}"));
		assertEquals("", p.getReplacer().process("${if;(a!=A)}"));

		assertEquals("true", p.getReplacer().process("${if;(a=${a})}"));

		assertEquals("true", p.getReplacer().process("${if;(a>=A)}"));
		assertEquals("true", p.getReplacer().process("${if;(a<=A)}"));
		assertEquals("", p.getReplacer().process("${if;(a<=${b})}"));

	}

	public void testFilterSubExpression() throws Exception {
		Processor p = new Processor();
		p.setProperty("a", "A");
		p.setProperty("b", "1");

		assertEquals("true", p.getReplacer().process("${if;(&(a=A)(b=1))}"));
		assertEquals("true", p.getReplacer().process("${if;(&(a=A)(b=1)(|(a!=A)(a=A)))}"));
	}

	public void testFilterWithArrays() throws Exception {
		Processor p = new Processor();
		p.setProperty("a", "A,B,C,D");
		p.setProperty("b", "1");

		assertEquals("", p.getReplacer().process("${if;(a=A)}"));
		assertEquals("true", p.getReplacer().process("${if;(a[]=A)}"));
		assertEquals("true", p.getReplacer().process("${if;(a[]=B)}"));
		assertEquals("true", p.getReplacer().process("${if;(a[]=D)}"));
		assertEquals("", p.getReplacer().process("${if;(a[]=E)}"));
		assertEquals("", p.getReplacer().process("${if;(a[]!=E)}"));
	}

	public void testFilterWithInheritance() throws Exception {
		Processor p = new Processor();
		Processor p1 = new Processor(p);
		Processor p2 = new Processor(p1);
		p.setProperty("a", "A,B,C,D");
		p.setProperty("b", "1");

		assertEquals("", p.getReplacer().process("${if;(a=A)}"));
		assertEquals("true", p.getReplacer().process("${if;(a[]=A)}"));
		assertEquals("true", p.getReplacer().process("${if;(a[]=B)}"));
		assertEquals("true", p.getReplacer().process("${if;(a[]=D)}"));
		assertEquals("", p.getReplacer().process("${if;(a[]=E)}"));
		assertEquals("", p.getReplacer().process("${if;(a[]!=E)}"));
	}

	public void testFilterExpressionWithReplacement() throws Exception {
		Processor p = new Processor();
		p.setProperty("a", "A");
		p.setProperty("b", "1");

		assertEquals("YES", p.getReplacer().process("${if;(a=A);YES}"));
		assertEquals("", p.getReplacer().process("${if;(a!=A);YES}"));
		assertEquals("YES", p.getReplacer().process("${if;(a=A);YES;NO}"));
		assertEquals("NO", p.getReplacer().process("${if;(a!=A);YES;NO}"));
	}

	public void testUnknownMacroDelimeters() throws IOException {
		Processor p = new Processor();
		assertEquals("${unknown}", p.getReplacer().process("${unknown}"));
		assertEquals("$<unknown>", p.getReplacer().process("$<unknown>"));
		assertEquals("$(unknown)", p.getReplacer().process("$(unknown)"));
		assertEquals("$[unknown]", p.getReplacer().process("$[unknown]"));
		assertEquals("$«unknown»", p.getReplacer().process("$«unknown»"));
		assertEquals("$‹unknown›", p.getReplacer().process("$‹unknown›"));
		assertTrue(p.check("No translation found for macro: unknown"));
	}

	public void testVersionMaskWithTarget() throws IOException {
		Processor p = new Processor();
		assertEquals("${version;===;$<@>}", p.getReplacer().process("${version;===;$<@>}"));
		assertTrue(p.check());
	}

	public void testVersionMaskWithoutTarget() throws IOException {
		Processor p = new Processor();
		assertEquals("${version;===}", p.getReplacer().process("${version;===}"));
		assertTrue(p.check());
	}

	public void testVersionMask() throws IOException {
		Processor p = new Processor();
		assertEquals("1.2.3", p.getReplacer().process("${version;===;1.2.3}"));
		assertTrue(p.check());
	}

	public void testVersionMaskWithSetExplicitTarget() throws IOException {
		Processor p = new Processor();
		p.setProperty("@", "1.2.3");
		assertEquals("1.2.3", p.getReplacer().process("${version;===;${@}}"));
		assertTrue(p.check());
	}

	public void testVersionMaskWithSetTarget() throws IOException {
		Processor p = new Processor();
		p.setProperty("@", "1.2.3");
		assertEquals("1.2.3", p.getReplacer().process("${version;===}"));
		assertTrue(p.check());
	}

	public void testRangeWithSetTarget() throws IOException {
		Processor p = new Processor();
		p.setProperty("@", "1.2.3");
		assertEquals("[1.2.3,2.2.3)", p.getReplacer().process("${range;[===,+===)}"));
		assertTrue(p.check());
	}

	public void testRangeWithSetExplicitTarget() throws IOException {
		Processor p = new Processor();
		p.setProperty("@", "1.2.3");
		assertEquals("[1.2.3,2.2.3)", p.getReplacer().process("${range;[===,+===);${@}}"));
		assertTrue(p.check());
	}

	public void testRangeWithTarget() throws IOException {
		Processor p = new Processor();
		assertEquals("${range;[===,+===)}", p.getReplacer().process("${range;[===,+===)}"));
		assertTrue(p.check());
	}

	public void testRangeWithExplicitTarget() throws IOException {
		Processor p = new Processor();
		assertEquals("${range;[===,+===);${@}}", p.getReplacer().process("${range;[===,+===);${@}}"));
		assertTrue(p.check());
	}

	public void testGlobToRegExp() {
		Processor p = new Processor();
		Macro m = p.getReplacer();
		assertEquals(".*x", m.process("${glob;*x}"));
		assertEquals("(?!.*x)", m.process("${glob;!*x}"));
	}

	/**
	 * A macro to get an attribute from a package
	 * 
	 * @throws Exception
	 */

	public static void testPackageAttribute() throws Exception {
		Builder builder = new Builder();
		builder.addClasspath(IO.getFile("jar/osgi.jar"));
		builder.setExportPackage("org.osgi.service.event;foo=3");
		builder.setProperty("Header-Version", "${packageattribute;org.osgi.service.event}");
		builder.setProperty("Header-Foo", "${packageattribute;org.osgi.service.event;from:}");
		builder.build();
		assertTrue(builder.check());

		Manifest m = builder.getJar().getManifest();
		String value = m.getMainAttributes().getValue("Header-Version");
		assertEquals("1.0.1", value);
		value = m.getMainAttributes().getValue("Header-Foo");
		assertNotNull(value);
	}
	/*
	 * #722 ${cat;<file>} removes \ before a $
	 */

	public void testCat() {
		Processor b = new Processor();
		b.setProperty("tst", "${cat;testresources/macro/cat-test.txt}");
		String tst = b.getProperty("tst");
		assertEquals("This is a \\$ backslashed dollar\n", tst);
	}

	/*
	 * #761 Tstamp consistent
	 */

	public void testTstampConsistent() throws Exception {
		Processor top = new Processor();
		Processor base = new Processor(top);

		base.setProperty("time", "${tstamp;S}");
		String start = base.getProperty("time");
		Thread.sleep(10);
		String end = base.getProperty("time");
		assertFalse(start.equals(end));

		top.setProperty("_@tstamp", end);

		start = base.getProperty("time");
		assertTrue(start.equals(end));
		Thread.sleep(10);
		end = base.getProperty("time");
		assertTrue(start.equals(end));
	}

	/**
	 * Combine
	 */

	public void testFuntionMacrosAndReplace() throws Exception {
		Processor processor = new Processor();
		processor.setProperty("libs", "/lib/a.jar, /lib/b.jar");
		processor.setProperty("foo", "--${1}--");
		processor.setProperty("xlibs", "${replace;${libs};/lib/(.*).jar;$0=${foo;$1}}");

		assertEquals("/lib/a.jar=--a--, /lib/b.jar=--b--", processor.getProperty("xlibs"));
	}

	/**
	 * File name tests
	 * 
	 * @throws Exception
	 */

	public void testFileNameMacros() throws Exception {
		Processor processor = new Processor();
		File a = IO.getFile("testresources/testfilenamemacros.properties");
		processor.setProperties(a);

		File b = IO.getFile(processor._thisfile(new String[0]));
		assertEquals(a, b);

		assertEquals("properties", processor.getReplacer()._extension(new String[] {
				"", "testresources/testfilenamemacros.properties"
		}));

		assertEquals("testfilenamemacros.properties",
				processor.getReplacer().process("${basename;testfilenamemacros.properties}"));
		assertEquals("testfilenamemacros", processor.getReplacer().process("${stem;testfilenamemacros.properties}"));
	}

	/**
	 * List functions
	 */
	public void testMacroLists() throws Exception {
		Processor processor = new Processor();

		assertEquals("true", processor.getReplacer().process("${apply;isnumber;1,2,3,4}"));
		assertEquals("10", processor.getReplacer().process("${apply;sum;1,2,3,4}"));
		assertEquals("false", processor.getReplacer().process("${apply;isnumber;1,2,3,a,4}"));

		processor.setProperty("double", "${1}${1}");
		processor.setProperty("mulbyindex", "${js;${1}*${2}}");
		assertEquals("A,B,C,D,E,F", processor.getReplacer().process("${map;toupper;a, b, c, d, e, f}"));
		assertEquals("aa,bb,cc,dd,ee,ff", processor.getReplacer().process("${map;double;a, b, c, d, e, f}"));
		assertEquals("0,2,6,12,20,30,42,56,72,90",
				processor.getReplacer().process("${foreach;mulbyindex;1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"));

		assertEquals("6", processor.getReplacer().process("${size;a, b, c, d, e, f}"));
		assertEquals("0", processor.getReplacer().process("${size;}"));

		assertEquals("d", processor.getReplacer().process("${get;3;a, b, c, d, e, f}"));
		assertEquals("d", processor.getReplacer().process("${get;-3;a, b, c, d, e, f}"));
		assertEquals("f", processor.getReplacer().process("${get;-1;a, b, c, d, e, f}"));

		assertEquals("b,c", processor.getReplacer().process("${sublist;1;3;a, b, c, d, e, f}"));
		assertEquals("e,f", processor.getReplacer().process("${sublist;-1;-3;a, b, c, d, e, f}"));

		assertEquals("a", processor.getReplacer().process("${first;a, b, c, d, e, f}"));
		assertEquals("", processor.getReplacer().process("${first;}"));
		assertEquals("f", processor.getReplacer().process("${last;a, b, c, d, e, f}"));
		assertEquals("", processor.getReplacer().process("${last;}"));

		assertEquals("5", processor.getReplacer().process("${indexof;6;1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"));
		assertEquals("-1", processor.getReplacer().process("${indexof;60;1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"));

		assertEquals("9", processor.getReplacer().process("${lastindexof;7;1, 2, 3, 4, 5, 6, 7, 7, 7, 10}"));

		assertEquals("10,9,8,7,6,5,4,3,2,1",
				processor.getReplacer().process("${reverse;1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"));

		assertEquals("55", processor.getReplacer().process("${sum;1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"));
		assertEquals("55", processor.getReplacer().process("${sum;1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"));

		assertEquals("5.5", processor.getReplacer().process("${average;1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"));

		assertEquals("-16", processor.getReplacer().process("${nmin;2, 0, -13, 40, 55, -16, 700, -8, 9, 10}"));
		assertEquals("-16", processor.getReplacer().process("${nmin;2; 0; -13; 40 ; 55 ; -16; 700; -8; 9; 10}"));

		assertEquals("700", processor.getReplacer().process("${nmax;2; 0, -13, 40, 55, -16, 700, -8, 9, 10}"));
		assertEquals("700", processor.getReplacer().process("${nmax;2; 0; -13; 40; 55; -16; 700, -8, 9, 10}"));

		assertEquals("-13", processor.getReplacer().process("${min;2; 0; -13; 40; 55; -16; 700, -8, 9, 10}"));
		assertEquals("9", processor.getReplacer().process("${max;2; 0, -13, 40, 55, -16, 700, -8, 9, 10}"));
	}

	/**
	 * String functions
	 */

	public void testMacroStrings() throws Exception {
		Processor processor = new Processor();
		processor.setProperty("empty", "");

		assertEquals("6", processor.getReplacer().process("${length;abcdef}"));

		assertEquals("true", processor.getReplacer().process("${is;1.3;1.3;1.3}"));
		assertEquals("false", processor.getReplacer().process("${is;abc;1.3}"));

		assertEquals("true", processor.getReplacer().process("${isnumber;1.3}"));
		assertEquals("false", processor.getReplacer().process("${isnumber;abc}"));

		assertEquals("true", processor.getReplacer().process("${isempty;${empty}}"));
		assertEquals("true", processor.getReplacer().process("${isempty;${empty};${empty};${empty};${empty};}"));
		assertEquals("false", processor.getReplacer().process("${isempty;abc}"));

		assertEquals("\n000010", processor.getReplacer().process("${format;\n%06d;10}"));
		assertEquals("000010", processor.getReplacer().process("${format;%1$06d;10}"));
		assertEquals("2e C8 300 620", processor.getReplacer().process("${format;%x %X %d %o;46;200;300;400;500}"));
		assertEquals("+00010", processor.getReplacer().process("${format;%+06d;10}"));
		assertEquals(String.format("%,6d", 100000), processor.getReplacer().process("${format;%,6d;100000}"));

		assertEquals("xyz", processor.getReplacer().process("${trim; \txyz\t  }"));

		assertEquals("DEFbDEFcdDEFef", processor.getReplacer().process("${subst;abacdaef;a;DEF}"));
		assertEquals("DEFbacdaef", processor.getReplacer().process("${subst;abacdaef;a;DEF;1}"));
		assertEquals("DEFbDEFcdaef", processor.getReplacer().process("${subst;abacdaef;a;DEF;2}"));
		assertEquals("DEFbDEFcdDEFef", processor.getReplacer().process("${subst;abacdaef;a;DEF;3}"));
		assertEquals("DEFbDEFcdDEFef", processor.getReplacer().process("${subst;abacdaef;a;DEF;300}"));

		assertEquals("true", processor.getReplacer().process("${matches;aaaabcdef;[a]+bcdef}"));
		assertEquals("false", processor.getReplacer().process("${matches;bcdef;[a]+bcdef}"));

		assertEquals("-1", processor.getReplacer().process("${ncompare;2;200}"));
		assertEquals("1", processor.getReplacer().process("${ncompare;200;1}"));
		assertEquals("0", processor.getReplacer().process("${ncompare;200;200}"));

		assertEquals("-1", processor.getReplacer().process("${compare;abc;def}"));
		assertEquals("1", processor.getReplacer().process("${compare;def;abc}"));
		assertEquals("0", processor.getReplacer().process("${compare;abc;abc}"));

		assertEquals("ABCDEF", processor.getReplacer().process("${toupper;abcdef}"));
		assertEquals("abcdef", processor.getReplacer().process("${tolower;ABCDEF}"));

		assertEquals("ab,efab,ef", processor.getReplacer().process("${split;cd;abcdefabcdef}"));
		assertEquals("ab,d,fab,d,f", processor.getReplacer().process("${split;[ce];abcdefabcdef}"));

		assertEquals("3", processor.getReplacer().process("${find;abcdef;def}"));
		assertEquals("-1", processor.getReplacer().process("${find;abc;defxyz}"));
		assertEquals("9", processor.getReplacer().process("${findlast;def;abcdefabcdef}"));

		assertEquals("abcdef", processor.getReplacer().process("${startswith;abcdef;abc}"));
		assertEquals("", processor.getReplacer().process("${startswith;abcdef;xyz}"));

		assertEquals("abcdef", processor.getReplacer().process("${endswith;abcdef;def}"));
		assertEquals("", processor.getReplacer().process("${endswith;abcdef;xyz}"));

		assertEquals("abcdef", processor.getReplacer().process("${endswith;abcdef;def}"));
		assertEquals("", processor.getReplacer().process("${endswith;abcdef;xyz}"));

		assertEquals("def", processor.getReplacer().process("${extension;abcdef.def}"));
		assertEquals("", processor.getReplacer().process("${extension;abcdefxyz}"));

		assertEquals("abc", processor.getReplacer().process("${substring;abcdef;0;3}"));
		assertEquals("abc", processor.getReplacer().process("${substring;abcdef;;3}"));
		assertEquals("def", processor.getReplacer().process("${substring;abcdef;-3}"));
		assertEquals("de", processor.getReplacer().process("${substring;abcdef;-3;-1}"));
		assertEquals("def", processor.getReplacer().process("${substring;abcdef;3}"));

		assertEquals("6", processor.getReplacer().process("${length;abcdef}"));
		assertEquals("0", processor.getReplacer().process("${length;}"));

	}

	/**
	 * Test rand
	 */

	public void testRan() {
		Processor processor = new Processor();
		for (int i = 0; i < 1000; i++) {
			int value = Integer.parseInt(processor.getReplacer().process("${rand;-10;10}"));
			assertTrue(value >= -10 && value <= 10);
		}
	}

	/**
	 * Test Javascript stuff
	 */

	public void testJSSimple() {
		Processor processor = new Processor();
		processor.setProperty("alpha", "25");
		assertEquals("3", processor.getReplacer().process("${js;1+2;}"));
		assertEquals("25", processor.getReplacer().process("${js;domain.get('alpha');}"));
		assertEquals("5", processor.getReplacer().process("${js;domain.get('alpha')/5;}"));

	}

	/**
	 * Check if we can initialize
	 */
	public void testJSINit() {
		Processor processor = new Processor();
		processor.setProperty("javascript", "function top() { return 13; }");
		assertEquals("16", processor.getReplacer().process("${js;1+2+top()}"));
	}

	/**
	 * See if the initcode is concatenated correctly
	 */
	public void testJSINit2() {
		Processor processor = new Processor();
		processor.setProperty("javascript", "function top() { return 1; }");
		processor.setProperty("javascript.1", "function top() { return 2; }");
		processor.setProperty("javascript.2", "function top() { return 3; }");
		assertEquals("3", processor.getReplacer().process("${js;top()}"));
	}

	/**
	 * Test control characters
	 */
	public void testControlCharacters() throws Exception {
		Processor p = new Processor();
		p.setProperty("a", "a, b, c");
		String s = p.getReplacer().process("${unescape;${replace;${a};(.+);$0;\\n}}\n");
		assertEquals("a\nb\nc\n", s);
	}

	/**
	 * Test the custom macros
	 */

	public void testCustomMacros() {
		Processor x = new Processor();
		x.setProperty("foo", "Hello ${1}");
		assertEquals("Hello Peter", x.getReplacer().process("${foo;Peter}"));

		assertTemplate("this is 1 abc, and this is def", "this is 1 ${1}, and this is ${2}", "abc;def");
		assertTemplate("abc,def", "${#}", "abc;def");
		assertTemplate("osgi.ee;filter:='(&(osgi.ee=JavaSE)(version=1.6))'",
				"osgi.ee;filter:='(&(osgi.ee=JavaSE)(version=1.${1}))'", "6");
	}

	void assertTemplate(String result, String template, String params) {
		Processor top = new Processor();
		top.setProperty("template", template);
		top.setProperty("macro", "${template;" + params + "}");
		String expanded = top.getProperty("macro");
		assertEquals(result, expanded);
	}

	/**
	 * Test replacement of ./ with cwd
	 */

	public static void testCurrentWorkingDirectory() {
		Processor top = new Processor();
		top.setProperty("cwd.1", "./"); // empty
		top.setProperty("cwd.2", " ./"); // empty
		top.setProperty("cwd.3", "./ "); // empty
		top.setProperty("cwd.4", " ./ "); // empty
		top.setProperty("cwd.5", "|./|"); // empty
		top.setProperty("cwd.6", "/.//"); // empty
		top.setProperty("cwd.7", "."); // empty
		top.setProperty("cwd.8", " . "); // empty
		top.setProperty("cwd.9", " . /"); // empty
		top.setProperty("cwd.10", " ."); // empty
		top.setProperty("cwd.11", "| ./|"); // empty
		top.setProperty("cwd.12", "|\t./|"); // empty
		top.setProperty("cwd.13", "|\r./|"); // empty
		top.setProperty("cwd.14", "|\n./|"); // empty

		String cwd = top.getBase().getAbsolutePath() + "/";

		assertEquals(" . ", top.getProperty("cwd.8"));
		assertEquals(cwd, top.getProperty("cwd.1"));
		assertEquals(" " + cwd, top.getProperty("cwd.2"));
		assertEquals(cwd + " ", top.getProperty("cwd.3"));
		assertEquals(" " + cwd + " ", top.getProperty("cwd.4"));
		assertEquals("|./|", top.getProperty("cwd.5"));
		assertEquals("/.//", top.getProperty("cwd.6"));
		assertEquals(".", top.getProperty("cwd.7"));
		assertEquals(" . /", top.getProperty("cwd.9"));
		assertEquals(" .", top.getProperty("cwd.10"));
		assertEquals("| " + cwd + "|", top.getProperty("cwd.11"));
		assertEquals("|\t" + cwd + "|", top.getProperty("cwd.12"));
		assertEquals("|\r" + cwd + "|", top.getProperty("cwd.13"));
		assertEquals("|\n" + cwd + "|", top.getProperty("cwd.14"));
	}

	/**
	 * Test if $if accepts isdir
	 */

	public static void testifDir() {
		Processor top = new Processor();
		top.setProperty("presentd", "${if;${isdir;jar};YES;NO}");
		top.setProperty("absentd", "${if;${isdir;xxx};YES;NO}");
		top.setProperty("wrongd", "${if;${isdir;bnd.bnd};YES;NO}");
		assertEquals("YES", top.getProperty("presentd"));
		assertEquals("NO", top.getProperty("wrongd"));
		assertEquals("NO", top.getProperty("absentd"));
		top.setProperty("presentf", "${if;${isfile;bnd.bnd};YES;NO}");
		top.setProperty("absentf", "${if;${isfile;xxx};YES;NO}");
		top.setProperty("wrongf", "${if;${isfile;jar};YES;NO}");
		assertEquals("YES", top.getProperty("presentf"));
		assertEquals("NO", top.getProperty("absentf"));
		assertEquals("NO", top.getProperty("wrongf"));
	}

	/**
	 * Test the combine macro that groups properties
	 */

	public static void testWildcardKeys() {
		Processor top = new Processor();
		top.setProperty("a.3", "a.3");
		top.setProperty("a.1", "a.1");
		top.setProperty("a.2", "a.2");
		top.setProperty("a.4", "a.4");
		top.setProperty("aa", "${a.*}");
		assertEquals("a.1,a.2,a.3,a.4", top.getProperty("a.*"));
		assertEquals("a.1,a.2,a.3,a.4", top.getProperty("aa"));

	}

	public static void testEnv() {
		Processor proc = new Processor();
		String s = proc.getReplacer().process("${env;PATH}");
		assertNotNull(s);
		assertTrue(s.length() > 0);
	}

	public static void testEnvAlt() {
		Processor proc = new Processor();
		String s = proc.getReplacer().process("${env;FOOBAR;hello}");
		assertEquals("hello", s);
	}
	/**
	 * Test the random macro
	 */
	public static void testRandom() {
		Processor top = new Processor();
		top.setProperty("a", "${random}");
		top.setProperty("a12", "${random;12}");
		String a = top.getProperty("a");
		System.err.println(a);
		assertEquals(8, a.length());
		String a12 = top.getProperty("a12");
		System.err.println(a12);
		assertEquals(12, a12.length());
		assertNotSame(a, a12);
	}


	/**
	 * Testing an example with nesting that was supposd not to work
	 */

	public static void testSuper() {
		Processor top = new Processor();
		Processor middle = new Processor(top);
		Processor bottom = new Processor(middle);

		top.setProperty("a", "top.a");
		top.setProperty("b", "top.b");
		top.setProperty("c", "top.c");
		top.setProperty("Bundle-Version", "0.0.0");
		middle.setProperty("a", "middle.a");
		middle.setProperty("b", "${^a}");
		middle.setProperty("c", "-${^c}-");
		middle.setProperty("Bundle-Version", "${^Bundle-Version}");
		assertEquals("middle.a", bottom.getProperty("a"));
		assertEquals("top.a", bottom.getProperty("b"));
		assertEquals("-top.c-", bottom.getProperty("c"));
		assertEquals("0.0.0", bottom.getProperty("Bundle-Version"));
	}

	/**
	 * Testing an example with nesting that was supposd not to work
	 */

	public static void testNesting2() {
		Processor p = new Processor();
		p.setProperty("groupId", "com.trivadis.tomas");
		p.setProperty("artifactId", "common");
		p.setProperty("bsn", "${if;${symbolicName};${symbolicName};${groupId}.${artifactId}}");
		p.setProperty("Bundle-SymbolicName", "${bsn}");
		p.setProperty("symbolicName", "");

		// Not set, so get the maven name
		assertEquals("com.trivadis.tomas.common", p.getProperty("Bundle-SymbolicName"));

		// Set it
		p.setProperty("symbolicName", "testing");
		assertEquals("testing", p.getProperty("Bundle-SymbolicName"));

		// And remove it
		p.setProperty("symbolicName", "");
		assertEquals("com.trivadis.tomas.common", p.getProperty("Bundle-SymbolicName"));
	}

	/**
	 * Verify system command
	 */

	public static void testSystem() throws Exception {
		// disable this test on windows
		if (!"/".equals(File.separator))
			return;

		Processor p = new Processor();
		Macro macro = new Macro(p);
		assertEquals("Hello World", macro.process("${system;echo Hello World}"));
		assertTrue(macro.process("${system;wc;Hello World}").matches("\\s*[0-9]+\\s+[0-9]+\\s+[0-9]+\\s*"));
	}

	public static void testSystemFail() throws Exception {
		Processor p = new Processor();
		Macro macro = new Macro(p);
		String cmd = "${system;mostidioticcommandthatwillsurelyfail}";
		assertTrue(macro.process(cmd).startsWith("${system;"));
	}

	/**
	 * Verify system-allow-fail command
	 */

	public static void testSystemAllowFail() throws Exception {
		Processor p = new Processor();
		Macro macro = new Macro(p);
		assertEquals("", macro.process("${system-allow-fail;mostidioticcommandthatwillsurelyfail}"));
	}

	/**
	 * Check that variables override macros.
	 */
	public static void testPriority() {
		Processor p = new Processor();
		p.setProperty("now", "not set");
		Macro macro = new Macro(p);
		assertEquals("not set", macro.process("${now}"));

	}

	public static void testNames() {
		Processor p = new Processor();
		p.setProperty("a", "a");
		p.setProperty("aa", "aa");
		Macro macro = new Macro(p);

		assertEquals("aa", macro.process("${${a}${a}}"));
	}

	public static void testVersion() throws Exception {
		Processor proc = new Processor();
		Macro macro = new Macro(proc);
		assertEquals("1.0.0", macro.process("${version;===;1.0.0}"));
		assertEquals("1.0.1", macro.process("${version;==+;1.0.0}"));
		assertEquals("1.1.1", macro.process("${version;=++;1.0.0}"));
		assertEquals("2.1.1", macro.process("${version;+++;1.0.0}"));
		assertEquals("0.1.1", macro.process("${version;-++;1.0.0}"));
		assertEquals("0.1.1", macro.process("${version;-++;1.0.0}"));
		assertEquals("0.0.0", macro.process("${version;---;1.1.1}"));
		assertEquals("0.0", macro.process("${version;--;1.1.1}"));
		assertEquals("1", macro.process("${version;=;1.1.1}"));
		assertEquals("[1.1,1.2)", macro.process("[${version;==;1.1.1},${version;=+;1.1.1})"));
		assertEquals("1.1", macro.process("${version;==;1.1.1}"));
		assertEquals("0.1.0", macro.process("${version;=+0;0.0.1}"));
		assertEquals("1.0.0", macro.process("${version;+00;0.1.1}"));

		// Test implicit version
		proc.setProperty("@", "1.2.3");
		assertEquals("[1.2,1.3)", macro.process("[${version;==},${version;=+})"));
		assertEquals("1.2", macro.process("${version;==}"));
		assertEquals("1.3.0", macro.process("${version;=+0}"));
		assertEquals("2.0.0", macro.process("${version;+00}"));

		assertEquals(0, proc.getErrors().size());
		assertEquals(0, proc.getWarnings().size());

		//
		// Add the S modifier. If qualifier is SNAPSHOT, it will return a
		// maven version
		//

		assertEquals("1.2.3-SNAPSHOT", macro.process("${version;===S;1.2.3.SNAPSHOT}"));
		assertEquals("1.2.3-SNAPSHOT", macro.process("${version;===s;1.2.3.SNAPSHOT}"));
		assertEquals("1.2.3.SNAPSHOT", macro.process("${version;====;1.2.3.SNAPSHOT}"));
		assertEquals("1.2.3-SNAPSHOT", macro.process("${version;===S;1.2.3.BUILD-SNAPSHOT}"));
		assertEquals("1.2.3-SNAPSHOT", macro.process("${version;===s;1.2.3.BUILD-SNAPSHOT}"));
		assertEquals("1.2.3.BUILD-SNAPSHOT", macro.process("${version;====;1.2.3.BUILD-SNAPSHOT}"));
		assertEquals("1.2.3.X", macro.process("${version;===S;1.2.3.X}"));
		assertEquals("1.2.3", macro.process("${version;===s;1.2.3.X}"));
		assertEquals("1.2.3.X", macro.process("${version;====;1.2.3.X}"));
	}

	public static void testRange() throws Exception {
		Processor proc = new Processor();
		Macro macro = new Macro(proc);
		assertEquals("[1.0,1.0]", macro.process("${range;[==,==];1.0.0}"));
		assertEquals("[1.0.0,1.0.1]", macro.process("${range;[===,==+];1.0.0}"));
		assertEquals("[0.1.0,0.1.2)", macro.process("${range;[=+0,=++);0.0.1}"));
		assertEquals("[0.0.9,0.1.2)", macro.process("${range;[==9,=++);0.0.1}"));

		assertEquals(0, proc.getErrors().size());
		assertEquals(0, proc.getWarnings().size());

		proc.setProperty("@", "1.2.3");
		assertEquals("[1.0.0,2)", macro.process("${range;[=00,+)}"));

		proc.clear();
		macro.process("${range;=+0,=++;0.0.1}");
		assertEquals(1, proc.getErrors().size());
		assertEquals(1, proc.getWarnings().size());

		proc.clear();
		macro.process("${range;[+,=)}");
		assertEquals(1, proc.getErrors().size());
		assertEquals(1, proc.getWarnings().size());
	}

	/**
	 * Test the wc function
	 */

	public static void testWc() {
		Processor p = new Processor();
		Macro macro = new Macro(p);
		String a = macro.process("${lsr;src/test;*.java}");
		assertTrue(a.contains("MacroTest.java"));
		assertTrue(a.contains("ManifestTest.java"));
		assertFalse(a.contains("bnd.info"));
		assertFalse(a.contains("com.acme"));
		assertFalse(a.contains("src/test/MacroTest.java"));
		assertFalse(a.contains("src/test/ManifestTest.java"));

		String b = macro.process("${lsa;src/test;*.java}");
		assertTrue(b.contains("src/test/MacroTest.java"));
		assertTrue(b.contains("src/test/ManifestTest.java"));
	}

	/**
	 * Check the uniq command
	 */

	public static void testUniq() {
		Builder builder = new Builder();
		Properties p = new Properties();
		p.setProperty("a", "${uniq;1}");
		p.setProperty("b", "${uniq;1,2}");
		p.setProperty("c", "${uniq;1;2}");
		p.setProperty("d", "${uniq;1; 1,  2 , 3}");
		p.setProperty("e", "${uniq;1; 1 , 2 ;      3;3,4,5,6}");
		builder.setProperties(p);
		assertEquals("1,2,3", builder.getProperty("d"));
		assertEquals("1,2", builder.getProperty("b"));
		assertEquals("1", builder.getProperty("a"));
		assertEquals("1,2", builder.getProperty("c"));
		assertEquals("1,2,3", builder.getProperty("d"));
		assertEquals("1,2,3,4,5,6", builder.getProperty("e"));

	}

	/**
	 * Test arguments with difficult characters like ;
	 */

	public static void testEscapedArgs() {
		Builder builder = new Builder();
		Properties p = new Properties();
		p.setProperty("x", "${replace;1,2,3;.+;$0\\;version=1}");
		builder.setProperties(p);
		assertEquals("1;version=1, 2;version=1, 3;version=1", builder.getProperty("x"));

	}

	/**
	 * Check if variables that contain variables, ad nauseum, really wrk
	 */
	public static void testNested() {
		Builder builder = new Builder();
		Properties p = new Properties();
		p.setProperty("a", ".");
		p.setProperty("b", "${a}");
		p.setProperty("c", "${b}");

		p.setProperty("d", "${tstamp;${format};UTC;${aug152008}}");
		p.setProperty("format", "yyyy");
		p.setProperty("aug152008", "1218810097322");

		p.setProperty("f", "${d}");
		p.setProperty("aug152008", "1218810097322");

		builder.setProperties(p);
		assertEquals(".", builder.getProperty("c"));
		assertEquals("2008", builder.getProperty("d"));
		assertEquals(builder.getProperty("f"), builder.getProperty("d"));
	}

	public static void testLoop() {
		Builder builder = new Builder();
		Properties p = new Properties();
		p.setProperty("a", "${b}");
		p.setProperty("b", "${a}");

		p.setProperty("d", "${e}");
		p.setProperty("e", "${f}");
		p.setProperty("f", "${g}");
		p.setProperty("g", "${h}");
		p.setProperty("h", "${d}");

		builder.setProperties(p);
		assertEquals("${infinite:[a,b,${b}]}", builder.getProperty("a"));
		assertEquals("${infinite:[d,h,g,f,e,${e}]}", builder.getProperty("d"));
	}

	public static void testTstamp() {
		String aug152008 = "1218810097322";
		Processor p = new Processor();
		Macro m = new Macro(p);
		assertEquals("200808151421", m.process("${tstamp;yyyyMMddHHmm;UTC;" + aug152008 + "}"));
		assertEquals("200808151521", m.process("${tstamp;yyyyMMddHHmm;GMT+01;" + aug152008 + "}"));
		assertEquals("2008", m.process("${tstamp;yyyy;UTC;" + aug152008 + "}"));

		// Why Tokyo? Japan doesn't use daylight savings, so the test shouldn't
		// break when clocks change.
		assertEquals("200808152321", m.process("${tstamp;yyyyMMddHHmm;Asia/Tokyo;" + aug152008 + "}"));
	}

	public static void testIsfile() {
		Processor p = new Processor();
		Macro m = new Macro(p);
		assertEquals("true", m.process("${isfile;.project}"));
		assertEquals("false", m.process("${isfile;thisfiledoesnotexist}"));
	}

	public static void testParentFile() {
		Processor p = new Processor();
		Macro m = new Macro(p);
		assertTrue(m.process("${dir;.project}").endsWith("biz.aQute.bndlib.tests"));
	}

	public static void testBasename() {
		Processor p = new Processor();
		Macro m = new Macro(p);
		assertEquals("biz.aQute.bndlib.tests", m.process("${basename;${dir;.project}}"));
	}

	public static void testMavenVersionMacro() throws Exception {
		Builder builder = new Builder();
		Properties p = new Properties();
		p.setProperty("Export-Package", "org.objectweb.*;version=1.5-SNAPSHOT");
		builder.setProperties(p);
		builder.setClasspath(new File[] {
				IO.getFile("jar/asm.jar")
		});
		Jar jar = builder.build();
		Manifest manifest = jar.getManifest();
		String export = manifest.getMainAttributes().getValue("Export-Package");
		assertNotNull(export);
		assertTrue("Test snapshot version", export.contains("1.5.0.SNAPSHOT"));
	}

	/**
	 * Check if we can check for the defintion of a variable
	 */

	public static void testDef() {
		Processor p = new Processor();
		p.setProperty("set.1", "1");
		p.setProperty("set.2", "2");
		Macro m = new Macro(p);
		assertEquals("NO", m.process("${if;${def;set.3};YES;NO}"));
		assertEquals("YES", m.process("${if;${def;set.1};YES;NO}"));
		assertEquals("YES", m.process("${if;${def;set.2};YES;NO}"));
	}

	/**
	 * NEW
	 */
	public static void testReplace() {
		Processor p = new Processor();
		p.setProperty("specs", "a,b, c,    d");
		Macro m = new Macro(p);
		assertEquals("xay, xby, xcy, xdy", m.process("${replace;${specs};([^\\s]+);x$1y}"));
	}

	public static void testToClassName() {
		Processor p = new Processor();
		Macro m = new Macro(p);
		assertEquals("com.acme.test.Test", m.process("${toclassname;com/acme/test/Test.class}"));
		assertEquals("Test", m.process("$<toclassname;Test.class>"));
		assertEquals("Test,com.acme.test.Test", m.process("${toclassname;Test.class, com/acme/test/Test.class}"));
		assertEquals("", m.process("$(toclassname;Test)"));
		assertEquals("com/acme/test/Test.class", m.process("$[toclasspath;com.acme.test.Test]"));
		assertEquals("Test.class", m.process("${toclasspath;Test}"));
		assertEquals("Test.class,com/acme/test/Test.class", m.process("${toclasspath;Test,com.acme.test.Test}"));
	}

	public static void testFindPath() throws IOException {
		try (Analyzer analyzer = new Analyzer()) {
			analyzer.setJar(IO.getFile("jar/asm.jar"));
			Macro m = new Macro(analyzer);

			assertTrue(m.process("${findname;(.*)\\.class;$1.xyz}").indexOf("FieldVisitor.xyz,") >= 0);
			assertTrue(m.process("${findname;(.*)\\.class;$1.xyz}").indexOf("MethodVisitor.xyz,") >= 0);
			assertTrue(
					m.process("${findpath;(.*)\\.class}").indexOf("org/objectweb/asm/AnnotationVisitor.class,") >= 0);
			assertTrue(m.process("${findpath;(.*)\\.class}")
					.indexOf("org/objectweb/asm/ByteVector.class, org/objectweb/asm/ClassAdapter.class,") >= 0);
			assertEquals("META-INF/MANIFEST.MF", m.process("${findpath;META-INF/MANIFEST.MF}"));
			assertEquals("Label.class", m.process("${findname;Label\\..*}"));
			assertEquals("Adapter, Visitor, Writer", m.process("${findname;Method(.*)\\.class;$1}"));
		}
	}

	public static void testWarning() {
		Processor p = new Processor();
		p.setProperty("three", "333");
		p.setProperty("empty", "");
		p.setProperty("real", "true");
		Macro m = new Macro(p);

		m.process("    ${warning;xw;1;2;3 ${three}}");
		m.process("    ${error;xe;1;2;3 ${three}}");
		m.process("    ${if;1;$<a>}");

		assertTrue("xw", p.getWarnings().get(0).endsWith("xw"));
		assertTrue("1", p.getWarnings().get(1).endsWith("1"));
		assertTrue("2", p.getWarnings().get(2).endsWith("2"));
		assertTrue("3 333", p.getWarnings().get(3).endsWith("3 333"));

		assertTrue("xw", p.getErrors().get(0).endsWith("xe"));
		assertTrue("1", p.getErrors().get(1).endsWith("1"));
		assertTrue("2", p.getErrors().get(2).endsWith("2"));
		assertTrue("3 333", p.getErrors().get(3).endsWith("3 333"));
	}

	public static void testNestedReplace() {
		Processor p = new Processor();
		Macro m = new Macro(p);
		String value = m.process("xx$(replace;1.2.3-SNAPSHOT;(\\d(\\.\\d)+).*;$1)xx");
		System.err.println(p.getWarnings());
		assertEquals("xx1.2.3xx", value);

		assertEquals("xx1.222.3xx", m.process("xx$(replace;1.222.3-SNAPSHOT;(\\d+(\\.\\d+)+).*;$1)xx"));

		p.setProperty("a", "aaaa");
		assertEquals("[cac]", m.process("$[replace;acaca;a(.*)a;[$1]]"));
		assertEquals("xxx", m.process("$(replace;yxxxy;[^x]*(x+)[^x]*;$1)"));
		assertEquals("xxx", m.process("$(replace;yxxxy;([^x]*(x+)[^x]*);$2)"));

	}

	public static void testParentheses() {
		Processor p = new Processor();
		Macro m = new Macro(p);
		String value = m.process("$(replace;();(\\(\\));$1)");
		assertEquals("()", value);
	}

	public static void testSimple() {
		Processor p = new Processor();
		p.setProperty("a", "aaaa");
		Macro m = new Macro(p);
		assertEquals("aaaa", m.process("${a}"));
		assertEquals("aaaa", m.process("$<a>"));
		assertEquals("aaaa", m.process("$(a)"));
		assertEquals("aaaa", m.process("$[a]"));

		assertEquals("xaaaax", m.process("x${a}x"));
		assertEquals("xaaaaxaaaax", m.process("x${a}x${a}x"));
	}

	public static void testFilter() {
		Processor p = new Processor();
		p.setProperty("a", "aaaa");
		Macro m = new Macro(p);
		assertEquals("aa,cc,ee", m.process("${filter;aa,bb,cc,dd,ee,ff;[ace]+}"));
		assertEquals("aaaa,cc,ee", m.process("${filter;${a},bb,cc,dd,ee,ff;[ace]+}"));
		assertEquals("bb,dd,ff", m.process("${filter;${a},bb,cc,dd,ee,ff;[^ace]+}"));
	}

	public static void testFilterOut() {
		Processor p = new Processor();
		p.setProperty("a", "aaaa");
		Macro m = new Macro(p);
		assertEquals("bb,dd,ff", m.process("${filterout;aa,bb,cc,dd,ee,ff;[ace]+}"));
		assertEquals("bb,dd,ff", m.process("${filterout;${a},bb,cc,dd,ee,ff;[ace]+}"));
		assertEquals("aaaa,cc,ee", m.process("${filterout;${a},bb,cc,dd,ee,ff;[^ace]+}"));
	}

	public static void testSort() {
		Processor p = new Processor();
		p.setProperty("a", "aaaa");
		Macro m = new Macro(p);
		assertEquals("aa,bb,cc,dd,ee,ff", m.process("${sort;aa,bb,cc,dd,ee,ff}"));
		assertEquals("aa,bb,cc,dd,ee,ff", m.process("${sort;ff,ee,cc,bb,dd,aa}"));
		assertEquals("aaaa,bb,cc,dd,ee,ff", m.process("${sort;ff,ee,cc,bb,dd,$<a>}"));
	}

	public static void testJoin() {
		Processor p = new Processor();
		p.setProperty("a", "aaaa");
		Macro m = new Macro(p);
		assertEquals("aa,bb,cc,dd,ee,ff", m.process("${join;aa,bb,cc,dd,ee,ff}"));
		assertEquals("aa,bb,cc,dd,ee,ff", m.process("${join;aa,bb,cc;dd,ee,ff}"));
		assertEquals("aa,bb,cc,dd,ee,ff", m.process("${join;aa;bb;cc;dd;ee,ff}"));

		assertEquals("aaXbbXccXddXeeXff", m.process("${sjoin;X;aa,bb,cc,dd,ee,ff}"));
		assertEquals("aa\nbb\ncc\ndd\nee\nff", m.process("${sjoin;\n;aa,bb,cc;dd,ee,ff}"));
		assertEquals("aa\nbb\ncc\ndd\nee\nff", m.process("${unescape;${sjoin;\\n;aa,bb,cc;dd,ee,ff}}"));
	}

	public static void testIf() {
		Processor p = new Processor();
		p.setProperty("a", "aaaa");
		Macro m = new Macro(p);
		assertEquals("aaaa", m.process("${if;1;$<a>}"));
		assertEquals("", m.process("${if;;$<a>}"));
		assertEquals("yes", m.process("${if;;$<a>;yes}"));
		assertEquals("yes", m.process("${if;false;$<a>;yes}"));
	}

	public static void testLiteral() {
		Processor p = new Processor();
		p.setProperty("a", "aaaa");
		Macro m = new Macro(p);
		assertEquals("${aaaa}", m.process("${literal;$<a>}"));
	}

	public static void testFilterout() throws Exception {
		Builder b = new Builder();
		b.addClasspath(IO.getFile("jar/osgi.jar"));
		b.addClasspath(IO.getFile("jar/ds.jar"));
		b.setProperty("Export-Package", "org.eclipse.*, org.osgi.*");
		b.setProperty("fwusers", "${classes;importing;org.osgi.framework}");
		b.setProperty("foo", "${filterout;${fwusers};org\\.osgi\\..*}");
		b.build();
		String fwusers = b.getProperty("fwusers");
		String foo = b.getProperty("foo");
		assertTrue(fwusers.length() > foo.length());
		assertTrue(fwusers.indexOf("org.osgi.framework.ServicePermission") >= 0);
		assertTrue(fwusers.indexOf("org.eclipse.equinox.ds.instance.BuildDispose") >= 0);
		assertFalse(foo.indexOf("org.osgi.framework.ServicePermission") >= 0);
		assertTrue(foo.indexOf("org.eclipse.equinox.ds.instance.BuildDispose") >= 0);
		System.err.println(b.getProperty("fwusers"));
		System.err.println(b.getProperty("foo"));

	}

	public static void testPackagesMacro() throws Exception {
		Builder b = new Builder();
		b.setClasspath(new Jar[] {
				new Jar(IO.getFile("bin"))
		});
		b.setProperty("Private-Package",
				"test.packageinfo.annotated,test.packageinfo.notannotated,test.packageinfo.nopackageinfo,test.activator");
		b.setProperty("All-Packages", "${packages}");
		b.setProperty("Annotated", "${packages;annotated;test.packageinfo.annotated.BlahAnnotation}");
		b.setProperty("Named", "${packages;named;*.notannotated}");
		b.setProperty("Negated", "${packages;named;!*.no*}");
		b.setProperty("Versioned", "${packages;versioned}");
		b.build();

		assertEquals(0, b.getErrors().size());

		assertEquals(
				"test.packageinfo.annotated,test.packageinfo.notannotated,test.packageinfo.nopackageinfo,test.activator",
				b.getProperty("All-Packages"));
		assertEquals("test.packageinfo.annotated", b.getProperty("Annotated"));
		assertEquals("test.packageinfo.notannotated", b.getProperty("Named"));
		assertEquals("test.packageinfo.annotated,test.activator", b.getProperty("Negated"));
		assertEquals("test.packageinfo.annotated,test.packageinfo.notannotated", b.getProperty("Versioned"));
	}

	public void testBase64() {
		Processor b = new Processor();
		b.setProperty("b64", "${base64;testresources/macro/base64-test.gif}");
		String b64 = "R0lGODlhBwAIAKIAANhCT91bZuN3gOeIkOiQl+ygp////////yH5BAEAAAcALAAAAAAHAAgAAAMXCLoqFUWoYogpKlgS8u4AZWGAAw0MkwAAOw==";
		assertEquals(b64, b.getProperty("b64"));
	}

	public void testDigest() {
		Processor b = new Processor();
		b.setProperty("a", "${digest;SHA-256;testresources/macro/digest-test.jar}");
		b.setProperty("b", "${digest;MD5;testresources/macro/digest-test.jar}");

		assertEquals("3B21F1450430C0AFF57E12A338EF6AA1A2E0EE318B8883DD196048450C2FC1FC", b.getProperty("a"));
		assertEquals("F31BAC7F1F70E5D8705B98CC0FBCFF5E", b.getProperty("b"));
	}

	public void testProcessNullValue() throws Exception {
		try (Processor b = new Processor()) {
			Macro m = b.getReplacer();
			String tst = m.process(null);
			assertEquals("", tst);
			assertTrue(b.check());
		}
	}

	public void testNonStringValue() throws Exception {
		try (Processor b = new Processor()) {
			// getProperty will return null for non-String value
			b.getProperties().put("tst", new StringBuilder("foo"));
			b.getProperties().put("num", 2);
			String tst = b.getProperty("tst");
			assertNull(tst);
			String num = b.getProperty("num");
			assertNull(num);
			assertTrue(b.check("Key 'tst' has a non-String value", "Key 'num' has a non-String value"));
		}
	}

	public void testNonStringFlattenedValue() throws Exception {
		try (Processor b = new Processor()) {
			// getProperty will return null for non-String value
			b.getProperties().put("tst", new StringBuilder("foo"));
			b.getProperties().put("num", 2);
			Properties f = b.getFlattenedProperties();
			String tst = f.getProperty("tst");
			assertNull(tst);
			String num = f.getProperty("num");
			assertNull(num);
			assertTrue(b.check("Key 'tst' has a non-String value", "Key 'num' has a non-String value"));
		}
	}
}
