diff options
7 files changed, 286 insertions, 8 deletions
diff --git a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java index d2772531c..4297e09ef 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java +++ b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/DirectorApplication.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2013 IBM Corporation and others. + * Copyright (c) 2007-2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,6 +12,7 @@ * Sonatype, Inc. - ongoing development * Pascal Rapicault - Support for bundled macosx http://bugs.eclipse.org/57349 * Red Hat, Inc. - support repositories passed via fragments (see bug 378329). + * SAP AG - list formatting (bug 423538) *******************************************************************************/ package org.eclipse.equinox.internal.p2.director.app; @@ -91,7 +92,7 @@ public class DirectorApplication implements IApplication, ProvisioningListener { } private static class CommandLineOption { - private final String[] identifiers; + final String[] identifiers; private final String optionSyntaxString; private final String helpString; @@ -127,6 +128,7 @@ public class DirectorApplication implements IApplication, ProvisioningListener { private static final CommandLineOption OPTION_HELP = new CommandLineOption(new String[] {"-help", "-h", "-?"}, null, Messages.Help_Prints_this_command_line_help); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ private static final CommandLineOption OPTION_LIST = new CommandLineOption(new String[] {"-list", "-l"}, Messages.Help_lb_lt_comma_separated_list_gt_rb, Messages.Help_List_all_IUs_found_in_repos); //$NON-NLS-1$ //$NON-NLS-2$ + private static final CommandLineOption OPTION_LIST_FORMAT = new CommandLineOption(new String[] {"-listFormat", "-lf"}, Messages.Help_lt_list_format_gt, Messages.Help_formats_the_IU_list); //$NON-NLS-1$ //$NON-NLS-2$ private static final CommandLineOption OPTION_LIST_INSTALLED = new CommandLineOption(new String[] {"-listInstalledRoots", "-lir"}, null, Messages.Help_List_installed_roots); //$NON-NLS-1$ //$NON-NLS-2$ private static final CommandLineOption OPTION_INSTALL_IU = new CommandLineOption(new String[] {"-installIU", "-installIUs", "-i"}, Messages.Help_lt_comma_separated_list_gt, Messages.Help_Installs_the_listed_IUs); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ private static final CommandLineOption OPTION_UNINSTALL_IU = new CommandLineOption(new String[] {"-uninstallIU", "-uninstallIUs", "-u"}, Messages.Help_lt_comma_separated_list_gt, Messages.Help_Uninstalls_the_listed_IUs); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ @@ -251,6 +253,7 @@ public class DirectorApplication implements IApplication, ProvisioningListener { private boolean printIUList = false; private boolean printRootIUList = false; private boolean printTags = false; + private IUListFormatter listFormat; private String revertToPreviousState = NOTHING_TO_REVERT_TO; private static String NOTHING_TO_REVERT_TO = "-1"; //$NON-NLS-1$ @@ -755,9 +758,9 @@ public class DirectorApplication implements IApplication, ProvisioningListener { } Collections.sort(allRoots); - for (IInstallableUnit iu : allRoots) { - System.out.println(iu.getId() + '=' + iu.getVersion()); - } + + String formattedString = listFormat.format(allRoots); + System.out.println(formattedString); } private void performProvisioningActions() throws CoreException { @@ -862,6 +865,12 @@ public class DirectorApplication implements IApplication, ProvisioningListener { continue; } + if (OPTION_LIST_FORMAT.isOption(opt)) { + String formatString = getRequiredArgument(args, ++i); + listFormat = new IUListFormatter(formatString); + continue; + } + if (OPTION_LIST_INSTALLED.isOption(opt)) { printRootIUList = true; continue; @@ -1016,13 +1025,24 @@ public class DirectorApplication implements IApplication, ProvisioningListener { } continue; } - throw new ProvisionException(NLS.bind(Messages.unknown_option_0, opt)); + + if (opt != null && opt.length() > 0) + throw new ProvisionException(NLS.bind(Messages.unknown_option_0, opt)); + } + + if (listFormat != null && !printIUList && !printRootIUList) { + throw new ProvisionException(NLS.bind(Messages.ArgRequiresOtherArgs, // + new String[] {OPTION_LIST_FORMAT.identifiers[0], OPTION_LIST.identifiers[0], OPTION_LIST_INSTALLED.identifiers[0]})); } - if (!printHelpInfo && !printIUList && !printRootIUList && !printTags && !purgeRegistry && rootsToInstall.isEmpty() && rootsToUninstall.isEmpty() && revertToPreviousState == NOTHING_TO_REVERT_TO) { + else if (!printHelpInfo && !printIUList && !printRootIUList && !printTags && !purgeRegistry && rootsToInstall.isEmpty() && rootsToUninstall.isEmpty() && revertToPreviousState == NOTHING_TO_REVERT_TO) { printMessage(Messages.Help_Missing_argument); printHelpInfo = true; } + + if (listFormat == null) { + listFormat = new IUListFormatter("${id}=${version}"); //$NON-NLS-1$ + } } /** @@ -1238,7 +1258,7 @@ public class DirectorApplication implements IApplication, ProvisioningListener { } private void performHelpInfo() { - CommandLineOption[] allOptions = new CommandLineOption[] {OPTION_HELP, OPTION_LIST, OPTION_LIST_INSTALLED, OPTION_INSTALL_IU, OPTION_UNINSTALL_IU, OPTION_REVERT, OPTION_DESTINATION, OPTION_DOWNLOAD_ONLY, OPTION_METADATAREPOS, OPTION_ARTIFACTREPOS, OPTION_REPOSITORIES, OPTION_VERIFY_ONLY, OPTION_TAG, OPTION_LIST_TAGS, OPTION_PROFILE, OPTION_FLAVOR, OPTION_SHARED, OPTION_BUNDLEPOOL, OPTION_PROFILE_PROPS, OPTION_IU_PROFILE_PROPS, OPTION_ROAMING, OPTION_P2_OS, OPTION_P2_WS, OPTION_P2_ARCH, OPTION_P2_NL, OPTION_PURGEHISTORY, OPTION_FOLLOW_REFERENCES}; + CommandLineOption[] allOptions = new CommandLineOption[] {OPTION_HELP, OPTION_LIST, OPTION_LIST_INSTALLED, OPTION_LIST_FORMAT, OPTION_INSTALL_IU, OPTION_UNINSTALL_IU, OPTION_REVERT, OPTION_DESTINATION, OPTION_DOWNLOAD_ONLY, OPTION_METADATAREPOS, OPTION_ARTIFACTREPOS, OPTION_REPOSITORIES, OPTION_VERIFY_ONLY, OPTION_TAG, OPTION_LIST_TAGS, OPTION_PROFILE, OPTION_FLAVOR, OPTION_SHARED, OPTION_BUNDLEPOOL, OPTION_PROFILE_PROPS, OPTION_IU_PROFILE_PROPS, OPTION_ROAMING, OPTION_P2_OS, OPTION_P2_WS, OPTION_P2_ARCH, OPTION_P2_NL, OPTION_PURGEHISTORY, OPTION_FOLLOW_REFERENCES}; for (int i = 0; i < allOptions.length; ++i) { allOptions[i].appendHelp(System.out); } diff --git a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/IUListFormatter.java b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/IUListFormatter.java new file mode 100644 index 000000000..67988f4fa --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/IUListFormatter.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2014 SAP AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * SAP AG - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.director.app; + +import java.util.*; +import java.util.regex.Pattern; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; + +/** + * Formats a set of {@link IInstallableUnit}s to a string specified + * by a format string. The format string's syntax is + * <code>${<property>}</code>, where <code><property></code> + * is one of the unit's {@link IInstallableUnit#getProperties() properties constants}. + * Two special properties are supported: + * <ul> + * <li>{@link IInstallableUnit#getId() IU id}: <code>${id}</code></li> + * <li>{@link IInstallableUnit#getVersion() IU version}: <code>${version}</code></li> + * </ul> + */ +public class IUListFormatter { + + private static final String PREFIX = "${"; //$NON-NLS-1$ + private static final String LINE_SEP = System.getProperty("line.separator"); //$NON-NLS-1$ + + private String formatString; + private Collection<String> properties; + + public IUListFormatter(String formatString) { + this.formatString = formatString; + this.properties = parse(formatString); + } + + public String format(Collection<IInstallableUnit> ius) { + StringBuilder result = new StringBuilder(); + for (IInstallableUnit iu : ius) { + format(iu, result); + } + + if (result.length() > 0) + result.setLength(result.length() - LINE_SEP.length()); //remove trailing newline + return result.toString(); + } + + private void format(IInstallableUnit iu, StringBuilder result) { + String s = formatString; + for (String property : properties) { + Pattern pattern = Pattern.compile(String.format("\\$\\{%s\\}", property)); //$NON-NLS-1$ + if ("id".equals(property)) { //$NON-NLS-1$ + s = insert(iu.getId(), pattern, s); + } else if ("version".equals(property)) { //$NON-NLS-1$ + s = insert(iu.getVersion().toString(), pattern, s); + } else { + String value = iu.getProperty(property, "df_LT"); //$NON-NLS-1$ + if (value == null) + value = ""; //$NON-NLS-1$ unknown property + s = insert(value, pattern, s); + } + } + + result.append(s); + result.append(LINE_SEP); + } + + private static String insert(String replacement, Pattern template, String s) { + return template.matcher(s).replaceAll(replacement); + } + + /* + * Finds all IU properties in the format string + */ + private static Collection<String> parse(String string) { + Set<String> properties = new HashSet<String>(5); + int start = 0; + while (start < string.length() && (start = string.indexOf(PREFIX, start)) > -1) { + int end = string.indexOf('}', start + PREFIX.length()); + if (end > start) { + String property = string.substring(start + PREFIX.length(), end); + properties.add(property); + start = end + 1; + } else { + // malformed input, be permissive and go on + start++; + } + } + return properties; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/Messages.java b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/Messages.java index 57a4058d4..fa98c1e4e 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/Messages.java +++ b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/Messages.java @@ -22,6 +22,8 @@ public class Messages extends NLS { public static String Application_NoManager; public static String Application_NoRepositories; + + public static String ArgRequiresOtherArgs; public static String Bad_format; public static String Cant_change_roaming; public static String destination_commandline; @@ -41,6 +43,8 @@ public class Messages extends NLS { public static String Help_List_all_IUs_found_in_repos; public static String Help_List_installed_roots; public static String Help_lt_comma_separated_list_gt; + + public static String Help_lt_list_format_gt; public static String Help_lb_lt_comma_separated_list_gt_rb; public static String Help_lt_name_gt; public static String Help_lt_path_gt; @@ -63,6 +67,8 @@ public class Messages extends NLS { public static String Help_List_Tags; public static String Help_Download_Only; + public static String Help_formats_the_IU_list; + public static String Ignored_repo; public static String Installing; public static String Missing_director; diff --git a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/messages.properties b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/messages.properties index bf50cda44..610ca7915 100644 --- a/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/messages.properties +++ b/bundles/org.eclipse.equinox.p2.director.app/src/org/eclipse/equinox/internal/p2/director/app/messages.properties @@ -29,6 +29,7 @@ Help_A_list_of_URLs_denoting_metadata_repositories=A list of URL's denoting meta Ambigous_Command=Cannot execute both {0} and {1} in one invocation. Application_NoManager=The repository manager is not available. Application_NoRepositories=Unable to load repositories. +ArgRequiresOtherArgs={0} requires either {1} or {2} Help_Defines_flavor_to_use_for_created_profile=Defines what flavor to use for a newly created profile. Help_Defines_what_profile_to_use_for_the_actions=Defines what profile to use for the actions. Help_Indicates_that_the_product_can_be_moved=Indicates that the product resulting from the installation can be moved. Effective only when a new profile is created. @@ -43,6 +44,7 @@ Help_List_all_IUs_found_in_repos=Lists all IU's found in the given repositories. Help_List_installed_roots=Lists all root IU's found in the given profile. Each entry in the list is in the form <id> [ '/' <version> ]. Help_Revert_to_previous_state=Revert the installation to a previous state [ the number representing the previous state of the profile as found in p2/org.eclipse.equinox.p2.engine/<profileId>/ ]. Help_lt_comma_separated_list_gt=<comma separated list> +Help_lt_list_format_gt=<list format string> Help_lb_lt_comma_separated_list_gt_rb=[ <comma separated list> ] Help_lt_name_gt=<name> Help_lt_path_gt=<path> @@ -59,6 +61,7 @@ Help_Follow_references=Follow repository references. Help_Defines_a_tag_for_provisioning_session=Tag the provisioning operation for easy referencing when reverting. Help_List_Tags=List the tags available Help_Download_Only=Only download the artifacts. +Help_formats_the_IU_list=Formats the list of IUs according to the given string. Use ${property} for variable parts, e.g. ${org.eclipse.equinox.p2.name} for the IU's name. ID and version of an IU are available through ${id} and ${version}. unable_to_parse_0_to_uri_1=Unable to parse {0} into an URI: {1}. unknown_option_0=Unknown option {0}. Use -help for a list of known options. Help_Missing_argument=The argument specifying what to install or uninstall is missing. diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/AllTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/AllTests.java index 0bb2c62f0..5765e897a 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/AllTests.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/AllTests.java @@ -29,6 +29,7 @@ public class AllTests extends TestCase { suite.addTestSuite(SingletonTest.class); suite.addTestSuite(UninstallTest.class); suite.addTestSuite(UpdateTest.class); + suite.addTestSuite(IUListFormatterTest.class); suite.addTestSuite(DirectorApplicationTest.class); return suite; } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java index c00ab4795..b8c6a8a94 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/DirectorApplicationTest.java @@ -11,6 +11,9 @@ *******************************************************************************/ package org.eclipse.equinox.p2.tests.director; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + import java.io.File; import java.io.PrintStream; import java.net.MalformedURLException; @@ -82,6 +85,13 @@ public class DirectorApplicationTest extends AbstractProvisioningTest { } /** + * creates the director app arguments with list arguments + */ + private String[] getSingleRepoArgsForListing(String message, File metadataRepo, File artifactRepo, String listArgument, String iuToList, String listFormatArgument, String formatString) { + return new String[] {"-metadataRepository", URIUtil.toUnencodedString(metadataRepo.toURI()), "-artifactRepository", URIUtil.toUnencodedString(artifactRepo.toURI()), listArgument, iuToList, listFormatArgument, formatString}; + } + + /** * creates the director app arguments based on the arguments submitted with bug 248045 but with multiple repositories for both metadata and artifacts */ private String[] getMultipleRepoArgs(String message, File metadataRepo1, File metadataRepo2, File artifactRepo1, File artifactRepo2, File destinationRepo, String installIU) { @@ -695,6 +705,42 @@ public class DirectorApplicationTest extends AbstractProvisioningTest { assertTrue(plugin2.exists()); } + public void testListFormatMissingListArgument() throws Exception { + //Setup: get repositories + File artifactRepo = getTestData("testListFormatMissingListArgument", "/testData/testRepos/updateSite"); + File metadataRepo = getTestData("testListFormatMissingListArgument", "/testData/testRepos/updateSite"); + + //Setup: create the args + String[] args = getSingleRepoArgsForListing("testListFormatMissingListArgument", metadataRepo, artifactRepo, "", "", "-listFormat", "%i=%v,%d"); + + StringBuffer buffer = runDirectorApp("testListFormatMissingListArgument", args); + assertThat(buffer.toString(), containsString("-listFormat requires")); + } + + public void testListFormat() throws Exception { + //Setup: get repositories + File artifactRepo = getTestData("testListFormat", "/testData/testRepos/updateSite"); + File metadataRepo = getTestData("testListFormat", "/testData/testRepos/updateSite"); + + //Setup: create the args + String[] args = getSingleRepoArgsForListing("testListFormat", metadataRepo, artifactRepo, "-list", "", "-listFormat", "${id}_${version},${id},${org.eclipse.equinox.p2.name}"); + + StringBuffer buffer = runDirectorApp("testListFormat", args); + assertThat(buffer.toString(), containsString("org.eclipse.ui.examples.job_3.0.0,org.eclipse.ui.examples.job,Progress Examples Plug-in")); + } + + public void testListNoExplicitFormat() throws Exception { + //Setup: get repositories + File artifactRepo = getTestData("testListNoExplicitFormat", "/testData/testRepos/updateSite"); + File metadataRepo = getTestData("testListNoExplicitFormat", "/testData/testRepos/updateSite"); + + //Setup: create the args + String[] args = getSingleRepoArgsForListing("testListNoExplicitFormat", metadataRepo, artifactRepo, "-list", "", "", ""); + + StringBuffer buffer = runDirectorApp("testListNoExplicitFormat", args); + assertThat(buffer.toString(), containsString("org.eclipse.ui.examples.job=3.0.0")); + } + /** * Test the ProvisioningContext only uses the passed in repos and not all known repos. * Expect to install helloworld_1.0.0 not helloworld_1.0.1 diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/IUListFormatterTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/IUListFormatterTest.java new file mode 100644 index 000000000..6bbbd46cd --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/director/IUListFormatterTest.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2014 SAP AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * SAP AG - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.director; + +import static java.util.Arrays.asList; +import static org.easymock.EasyMock.*; + +import java.util.*; +import junit.framework.TestCase; +import org.easymock.IAnswer; +import org.eclipse.equinox.internal.p2.director.app.IUListFormatter; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.metadata.Version; + +public class IUListFormatterTest extends TestCase { + + public void testFormat() { + IUListFormatter format = new IUListFormatter("${id}=${id},${version},${org.eclipse.equinox.p2.name}"); + String result = format.format(asList(createIU("iu1", "1.0.0", "name", null))); + assertEquals("iu1=iu1,1.0.0,name", result); + } + + public void testFormat_MultipleIUs() { + IUListFormatter format = new IUListFormatter("${id}=${version},${org.eclipse.equinox.p2.name}"); + String result = format.format(asList(// + createIU("iu1", "1.0.0", "name", null),// + createIU("iu2", "2.0.0", "name2", null)// + )); + assertEquals("iu1=1.0.0,name" + System.getProperty("line.separator") + "iu2=2.0.0,name2", result); + } + + public void testFormat_UnknownFormatOption() { + IUListFormatter format = new IUListFormatter("${id}${UNKNOWN}"); + String result = format.format(asList(createIU("iu1", "1.0.0", "name", null))); + assertEquals("iu1", result); + } + + public void testFormat_Malformed() { + IUListFormatter format = new IUListFormatter("${id=${version"); + String result = format.format(asList(createIU("iu1", "1.0.0", "name", null))); + assertEquals("Input must be preserved", "${id=${version", result); + } + + public static void main(String[] args) { + IUListFormatter format = new IUListFormatter("${id=${version"); + List<IInstallableUnit> ius = new ArrayList<IInstallableUnit>(20000); + for (int i = 0; i < 20000; i++) { + ius.add(createIU("iu_" + i, "1.0.0", 30)); + } + long start = System.currentTimeMillis(); + format.format(ius); + System.out.println(System.currentTimeMillis() - start); + } + + private static IInstallableUnit createIU(String id, String version, String name, String description) { + IInstallableUnit iu = createNiceMock(id, IInstallableUnit.class); + expect(iu.getId()).andStubReturn(id); + expect(iu.getVersion()).andStubReturn(Version.create(version)); + + final Map<String, String> properties = new HashMap<String, String>(3, 1); + properties.put(IInstallableUnit.PROP_NAME, name); + properties.put(IInstallableUnit.PROP_DESCRIPTION, description); + expect(iu.getProperties()).andStubReturn(properties); + expect(iu.getProperty((String) anyObject())).andStubAnswer(new MapAnswer(properties)); + expect(iu.getProperty((String) anyObject(), (String) anyObject())).andStubAnswer(new MapAnswer(properties)); + replay(iu); + return iu; + } + + private static IInstallableUnit createIU(String id, String version, int propCount) { + IInstallableUnit iu = createNiceMock(id, IInstallableUnit.class); + expect(iu.getId()).andStubReturn(id); + expect(iu.getVersion()).andStubReturn(Version.create(version)); + + final Map<String, String> properties = new HashMap<String, String>(propCount, 1); + for (int i = 0; i < propCount; i++) { + properties.put("prop_" + i, "propValue_" + i); + } + expect(iu.getProperties()).andStubReturn(properties); + expect(iu.getProperty((String) anyObject())).andStubAnswer(new MapAnswer(properties)); + expect(iu.getProperty((String) anyObject(), (String) anyObject())).andStubAnswer(new MapAnswer(properties)); + replay(iu); + return iu; + } + + private static final class MapAnswer<T> implements IAnswer<T> { + private final Map<?, T> map; + + MapAnswer(Map<?, T> map) { + this.map = map; + } + + public T answer() throws Throwable { + return map.get(getCurrentArguments()[0]); + } + } + +} |