summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCedric Beust <cedric@beust.com>2012-08-01 10:45:27 -0700
committerCedric Beust <cedric@beust.com>2012-08-01 10:45:27 -0700
commitdbccffb3047aa721e3c09ecda4e6b12a00152dd1 (patch)
tree32eb15ff497c92e696a9bf4201b12989f50b3f28
parent18acf239feebe9f079a4d31e1852556b7cd2fd4c (diff)
downloadjcommander-dbccffb3047aa721e3c09ecda4e6b12a00152dd1.tar.gz
Refactoring: introducing FuzzyMap and IKey.
-rw-r--r--src/main/java/com/beust/jcommander/FuzzyMap.java61
-rw-r--r--src/main/java/com/beust/jcommander/JCommander.java94
-rw-r--r--src/main/java/com/beust/jcommander/StringKey.java48
-rw-r--r--src/test/java/com/beust/jcommander/JCommanderTest.java2
-rw-r--r--src/test/resources/testng-single.xml8
5 files changed, 146 insertions, 67 deletions
diff --git a/src/main/java/com/beust/jcommander/FuzzyMap.java b/src/main/java/com/beust/jcommander/FuzzyMap.java
new file mode 100644
index 0000000..5f3939b
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/FuzzyMap.java
@@ -0,0 +1,61 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.internal.Maps;
+
+import java.util.Map;
+
+/**
+ * Helper class to perform fuzzy key look ups: looking up case insensitive or
+ * abbreviated keys.
+ */
+public class FuzzyMap {
+ interface IKey {
+ String getName();
+ }
+
+ public static <V> V findInMap(Map<? extends IKey, V> map, IKey name,
+ boolean caseSensitive, boolean allowAbbreviations) {
+ if (allowAbbreviations) {
+ return findAbbreviatedValue(map, name, caseSensitive);
+ } else {
+ if (caseSensitive) {
+ return map.get(name);
+ } else {
+ for (IKey c : map.keySet()) {
+ if (c.getName().equalsIgnoreCase(name.getName())) {
+ return map.get(c);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static <V> V findAbbreviatedValue(Map<? extends IKey, V> map, IKey name,
+ boolean caseSensitive) {
+ String string = name.getName();
+ Map<String, V> results = Maps.newHashMap();
+ for (IKey c : map.keySet()) {
+ String n = c.getName();
+ boolean match = (caseSensitive && n.startsWith(string))
+ || ((! caseSensitive) && n.toLowerCase().startsWith(string.toLowerCase()));
+ if (match) {
+ results.put(n, map.get(c));
+ }
+ }
+
+ V result;
+ if (results.size() > 1) {
+ throw new ParameterException("Ambiguous option: " + name
+ + " matches " + results.keySet());
+ } else if (results.size() == 1) {
+ result = results.values().iterator().next();
+ } else {
+ result = null;
+ }
+
+ return result;
+ }
+
+
+}
diff --git a/src/main/java/com/beust/jcommander/JCommander.java b/src/main/java/com/beust/jcommander/JCommander.java
index fccbb9e..bfda67f 100644
--- a/src/main/java/com/beust/jcommander/JCommander.java
+++ b/src/main/java/com/beust/jcommander/JCommander.java
@@ -18,6 +18,7 @@
package com.beust.jcommander;
+import com.beust.jcommander.FuzzyMap.IKey;
import com.beust.jcommander.converters.IParameterSplitter;
import com.beust.jcommander.converters.NoConverter;
import com.beust.jcommander.converters.StringConverter;
@@ -67,7 +68,7 @@ public class JCommander {
/**
* A map to look up parameter description per option name.
*/
- private Map<String, ParameterDescription> m_descriptions;
+ private Map<IKey, ParameterDescription> m_descriptions;
/**
* The objects that contain fields annotated with @Parameter.
@@ -122,7 +123,7 @@ public class JCommander {
/**
* Alias database for reverse lookup
*/
- private Map<String, ProgramName> aliasMap = Maps.newLinkedHashMap();
+ private Map<IKey, ProgramName> aliasMap = Maps.newLinkedHashMap();
/**
* The name of the command after the parsing has run.
@@ -399,8 +400,8 @@ public class JCommander {
}
private ParameterDescription getPrefixDescriptionFor(String arg) {
- for (Map.Entry<String, ParameterDescription> es : m_descriptions.entrySet()) {
- if (arg.startsWith(es.getKey())) return es.getValue();
+ for (Map.Entry<IKey, ParameterDescription> es : m_descriptions.entrySet()) {
+ if (arg.startsWith(es.getKey().getName())) return es.getValue();
}
return null;
@@ -539,14 +540,14 @@ public class JCommander {
new ParameterDescription(object, p, parameterized, m_bundle, this);
} else {
for (String name : p.names()) {
- if (m_descriptions.containsKey(name)) {
+ if (m_descriptions.containsKey(new StringKey(name))) {
throw new ParameterException("Found the option " + name + " multiple times");
}
p("Adding description for " + name);
ParameterDescription pd =
new ParameterDescription(object, p, parameterized, m_bundle, this);
m_fields.put(parameterized, pd);
- m_descriptions.put(name, pd);
+ m_descriptions.put(new StringKey(name), pd);
if (p.required()) m_requiredFields.put(parameterized, pd);
}
@@ -574,7 +575,7 @@ public class JCommander {
ParameterDescription pd =
new ParameterDescription(object, dp, parameterized, m_bundle, this);
m_fields.put(parameterized, pd);
- m_descriptions.put(name, pd);
+ m_descriptions.put(new StringKey(name), pd);
if (dp.required()) m_requiredFields.put(parameterized, pd);
}
@@ -1356,8 +1357,9 @@ public class JCommander {
//Note: Name clash check is intentionally omitted to resemble the
// original behaviour of clashing commands.
// Aliases are, however, are strictly checked for name clashes.
- aliasMap.put(name, progName);
- for (String alias : aliases) {
+ aliasMap.put(new StringKey(name), progName);
+ for (String a : aliases) {
+ IKey alias = new StringKey(a);
//omit pointless aliases to avoid name clash exception
if (!alias.equals(name)) {
ProgramName mappedName = aliasMap.get(alias);
@@ -1415,68 +1417,29 @@ public class JCommander {
return m_objects;
}
- private <V> V findAbbreviatedOption(Map<String, V> map, String name, boolean caseSensitive) {
- Map<String, V> results = Maps.newHashMap();
- for (String c : map.keySet()) {
- boolean match = (caseSensitive && c.startsWith(name))
- || ((! caseSensitive) && c.toLowerCase().startsWith(name.toLowerCase()));
- if (match) {
- results.put(c, map.get(c));
- }
- }
- V result;
- if (results.size() > 1) {
- throw new ParameterException("Ambiguous option: " + name
- + " matches " + results.keySet());
- } else if (results.size() == 1) {
- result = results.values().iterator().next();
- } else {
- result = null;
- }
-
- return result;
- }
-
- private <V> V findCaseSensitiveMap(Map<String, V> map, String name) {
- if (m_caseSensitiveOptions) {
- if (m_allowAbbreviatedOptions) {
- return findAbbreviatedOption(map, name, m_caseSensitiveOptions);
- } else {
- return map.get(name);
- }
- } else {
- if (m_allowAbbreviatedOptions) {
- return findAbbreviatedOption(map, name, m_caseSensitiveOptions);
- } else {
- for (String c : map.keySet()) {
- if (c.equalsIgnoreCase(name)) {
- return map.get(c);
- }
- }
- }
- }
- return null;
- }
-
private ParameterDescription findParameterDescription(String arg) {
- return findCaseSensitiveMap(m_descriptions, arg);
+ return FuzzyMap.findInMap(m_descriptions, new StringKey(arg), m_caseSensitiveOptions,
+ m_allowAbbreviatedOptions);
}
private JCommander findCommand(ProgramName name) {
- if (! m_caseSensitiveOptions) {
- return m_commands.get(name);
- } else {
- for (ProgramName c : m_commands.keySet()) {
- if (c.getName().equalsIgnoreCase(name.getName())) {
- return m_commands.get(c);
- }
- }
- }
- return null;
+ return FuzzyMap.findInMap(m_commands, name,
+ m_caseSensitiveOptions, m_allowAbbreviatedOptions);
+// if (! m_caseSensitiveOptions) {
+// return m_commands.get(name);
+// } else {
+// for (ProgramName c : m_commands.keySet()) {
+// if (c.getName().equalsIgnoreCase(name.getName())) {
+// return m_commands.get(c);
+// }
+// }
+// }
+// return null;
}
private ProgramName findProgramName(String name) {
- return findCaseSensitiveMap(aliasMap, name);
+ return FuzzyMap.findInMap(aliasMap, new StringKey(name),
+ m_caseSensitiveOptions, m_allowAbbreviatedOptions);
}
/*
@@ -1499,7 +1462,7 @@ public class JCommander {
/**
* Encapsulation of either a main application or an individual command.
*/
- private static final class ProgramName {
+ private static final class ProgramName implements IKey {
private final String m_name;
private final List<String> m_aliases;
@@ -1508,6 +1471,7 @@ public class JCommander {
m_aliases = aliases;
}
+ @Override
public String getName() {
return m_name;
}
diff --git a/src/main/java/com/beust/jcommander/StringKey.java b/src/main/java/com/beust/jcommander/StringKey.java
new file mode 100644
index 0000000..09d1149
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/StringKey.java
@@ -0,0 +1,48 @@
+package com.beust.jcommander;
+
+import com.beust.jcommander.FuzzyMap.IKey;
+
+public class StringKey implements IKey {
+
+ private String m_name;
+
+ public StringKey(String name) {
+ m_name = name;
+ }
+
+ @Override
+ public String getName() {
+ return m_name;
+ }
+
+ @Override
+ public String toString() {
+ return m_name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ StringKey other = (StringKey) obj;
+ if (m_name == null) {
+ if (other.m_name != null)
+ return false;
+ } else if (!m_name.equals(other.m_name))
+ return false;
+ return true;
+ }
+
+}
diff --git a/src/test/java/com/beust/jcommander/JCommanderTest.java b/src/test/java/com/beust/jcommander/JCommanderTest.java
index 741f16a..2f2d88d 100644
--- a/src/test/java/com/beust/jcommander/JCommanderTest.java
+++ b/src/test/java/com/beust/jcommander/JCommanderTest.java
@@ -861,7 +861,7 @@ public class JCommanderTest {
@Test(enabled = false)
public static void main(String[] args) throws Exception {
-// new JCommanderTest().ambiguousAbbreviatedOptionsCaseInsensitive();
+ new JCommanderTest().multiObjectsWithDuplicatesFail();
// class A {
// @Parameter(names = "-short", required = true)
// List<String> parameters;
diff --git a/src/test/resources/testng-single.xml b/src/test/resources/testng-single.xml
index c3cdd46..d981d70 100644
--- a/src/test/resources/testng-single.xml
+++ b/src/test/resources/testng-single.xml
@@ -4,7 +4,13 @@
<test name="JCommander tests">
<classes>
- <class name="com.beust.jcommander.FinderTest">
+<!--
+ <class name="com.beust.jcommander.FinderTest" />
+-->
+ <class name="com.beust.jcommander.command.CommandAliasTest">
+ <methods>
+ <include name="clashingAliasesAreNotAllowed" />
+ </methods>
</class>
</classes>
</test>