package gov.nasa.jpl.mbee.mdk.util; import org.junit.Assert; import org.junit.Test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * MoreToString adds options to toString() for recursively nested objects. */ public interface MoreToString { // Constants to be used as options for formatting collections and arrays. int NO_FORMAT = -1; int SQUARE_BRACES = 0; int CURLY_BRACES = 1; int PARENTHESES = 2; int COMMA = 3; int PERIOD = 4; int EQUALS = 5; String[] prefixes = new String[]{"[", "{", "(", "(", "(", "("}; String[] suffixes = new String[]{"]", "}", ")", ")", ")", ")"}; String[] delimiters = new String[]{",", ",", ",", ",", ".", "="}; Map<String, String[]> formatOptions = Helper.initFormatOptions(); /** * Write object recursively based on passed options. * * @param withHash whether to include "@" + hasCode() in the returned String. * @param deep whether to include member/child detail and call their * MoreToString.toString() (typically) with the same options. * @param seen whether the object has already been written with deep=true, in * which case it will set deep=false to end the recursion. * @return the string representation of the object. */ String toString(boolean withHash, boolean deep, Set<Object> seen); /** * Write object recursively based on passed options. * * @param withHash whether to include "@" + hasCode() in the returned String. * @param deep whether to include member/child detail and call their * MoreToString.toString() (typically) with the same options. * @param seen whether the object has already been written with deep=true, in * which case it will set deep=false to end the recursion. * @param otherOptions other class or context-specific options with names and values. * @return the string representation of the object. */ String toString(boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions); /** * @return a single name or value */ String toShortString(); /** * Helper class for MoreToString toString() calls. There are also functions * for formatting collections. */ class Helper { /** * Helper function for MoreToString.toString() when it is not known * whether the input object implements MoreToString. * * @return ((MoreToString)object).toString(...) with the same options * passed if the object does implement MoreToString; otherwise * return object.toString(). */ public static String toString(Object object, boolean withHash, boolean deep, Set<Object> seen, boolean checkIfMoreToString, Map<String, Object> otherOptions) { if (object == null) { return "null"; } // We have to make sure we get to the right method since collections // require an extra boolean argument. if (object.getClass().isArray()) { return toString(Arrays.asList(object), withHash, deep, seen, otherOptions); } if (object instanceof Collection) { return toString((Collection<?>) object, withHash, deep, seen, otherOptions, true); } if (object instanceof Map) { return toString((Map<?, ?>) object, withHash, deep, seen, otherOptions, true); } if (object instanceof Pair) { return toString((Pair<?, ?>) object, withHash, deep, seen, otherOptions, true); } if (object instanceof Map.Entry) { return toString((Map.Entry<?, ?>) object, withHash, deep, seen, otherOptions, true); } if (checkIfMoreToString && object instanceof MoreToString) { return ((MoreToString) object).toString(withHash, deep, seen, otherOptions); } return object.toString(); } /** * Helper function for MoreToString.toString() when it is not known * whether the input object implements MoreToString. * * @return ((MoreToString)object).toString(...) with the same options * passed if the object does implement MoreToString; otherwise * return object.toString(). */ public static String toString(Object object, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions) { return toString(object, withHash, deep, seen, true, otherOptions); } /** * Helper function for MoreToString.toString() when it is not known * whether the input object implements MoreToString. * * @return ((MoreToString)object).toString(...) with the same options * passed if the object does implement MoreToString; otherwise * return object.toString(). */ public static String toString(Object object, boolean withHash, boolean deep, Set<Object> seen) { return toString(object, withHash, deep, seen, null); } /** * Helper function for MoreToString.toString() when it is not known * whether the input object implements MoreToString and default * parameter values are fine. * * @param object * @return */ public static String toString(Object object) { // Below works for array or other non-collection object. return toString(object, false, false, null, null); } /** * Helper function for MoreToString.toString() when it is not known * whether the input object implements MoreToString and default * parameter values are fine. * * @param object * @return */ public static String toLongString(Object object) { // Below works for array or other non-collection object. return toString(object, true, true, null); } public static String toShortString(Object object) { if (object == null) { return "null"; } // We have to make sure we get to the right method since collections // require an extra boolean argument. if (object instanceof Collection) { return toShortString((Collection<?>) object, null, true); } if (object instanceof Map) { return toShortString((Map<?, ?>) object, null, true); } if (object instanceof Pair) { return toShortString((Pair<?, ?>) object, null, true); } if (object instanceof Map.Entry) { return toShortString((Map.Entry<?, ?>) object, null, true); } if (object instanceof MoreToString) { return ((MoreToString) object).toShortString(); } return object.toString(); } public static <T> String toShortString(Collection<T> collection, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = PARENTHESES; if (hasFormatOptions(otherOptions)) { formatKey = NO_FORMAT; } return toShortString(collection, formatKey, otherOptions, checkIfMoreToString); } public static <T> String toShortString(final Collection<T> collection, int formatKey, Map<String, Object> otherOptions, boolean checkIfMoreToString) { if (checkIfMoreToString && collection instanceof MoreToString) { return ((MoreToString) collection).toShortString(); } return toString(collection.toArray(), false, false, null, otherOptions, formatKey); } public static <K, V> String toShortString(Map<K, V> map, Map<String, Object> otherOptions, int formatKey, boolean checkIfMoreToString) { if (checkIfMoreToString && map instanceof MoreToString) { stuffOptionsFromKey(otherOptions, formatKey); return ((MoreToString) map).toShortString(); } return toString(map.entrySet().toArray(), false, false, null, otherOptions, formatKey); } public static <K, V> String toShortString(Map<K, V> map, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = CURLY_BRACES; if (hasFormatOptions(otherOptions)) { formatKey = NO_FORMAT; } return toString(map, false, false, null, otherOptions, formatKey, checkIfMoreToString); } public static <K, V> String toShortString(Map.Entry<K, V> entry, Map<String, Object> otherOptions, int formatKey, boolean checkIfMoreToString) { if (checkIfMoreToString && entry instanceof MoreToString) { stuffOptionsFromKey(otherOptions, formatKey); return ((MoreToString) entry).toShortString(); } Pair<K, V> p = new Pair<K, V>(entry.getKey(), entry.getValue()); return toShortString(p, otherOptions, formatKey, true); } public static <K, V> String toShortString(Map.Entry<K, V> entry, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = EQUALS; if (hasFormatOptions(otherOptions)) { formatKey = NO_FORMAT; }// else { return toString(entry, false, false, null, otherOptions, formatKey, checkIfMoreToString); // } } public static <K, V> String toShortString(Pair<K, V> pair, Map<String, Object> otherOptions, int formatKey, boolean checkIfMoreToString) { if (checkIfMoreToString && pair instanceof MoreToString) { stuffOptionsFromKey(otherOptions, formatKey); return ((MoreToString) pair).toShortString(); } return toString(new Object[]{pair.getKey(), pair.getValue()}, false, false, null, otherOptions, formatKey); } public static <K, V> String toShortString(Pair<K, V> pair, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = hasFormatOptions(otherOptions) ? NO_FORMAT : PARENTHESES; return toShortString(pair, otherOptions, formatKey, checkIfMoreToString); } public static <T> String toShortString(final T[] array, Map<String, Object> otherOptions) { return toString(array, false, false, null, otherOptions); } /** * Writes an array to a string with MoreToString options, including an * explicit format key. For example, if the format key * MoreToString.PARENTHESES is given, the collection will be enclosed in * parentheses, "(" and ")" and the default comma will be used as a * delimiter between elements. * * @param withHash whether to include "@" + hasCode() in the returned String. * @param deep whether to include member/child detail and call their * MoreToString.toString() (typically) with the same options. * @param seen whether the object has already been written with * deep=true, in which case it will set deep=false to end the * recursion. * @param otherOptions other class or context-specific options with names and * values. * @param formatKey a MoreToString constant indicating the grouping format of * the elements of the collection, for example, * MoreToString.CURLY_BRACES. * @return the string representation of the object. */ public static <T> String toString(final Collection<T> collection, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, int formatKey, boolean checkIfMoreToString) { if (checkIfMoreToString && collection instanceof MoreToString) { stuffOptionsFromKey(otherOptions, formatKey); return ((MoreToString) collection).toString(withHash, deep, seen, otherOptions); } return toString(collection.toArray(), withHash, deep, seen, otherOptions, formatKey); } public static <T> String toString(Collection<T> collection, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, boolean square, boolean checkIfMoreToString) { int formatKey = (square ? SQUARE_BRACES : PARENTHESES); return toString(collection, withHash, deep, seen, otherOptions, formatKey, checkIfMoreToString); } public static <T> String toString(Collection<T> collection, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = PARENTHESES; if (hasFormatOptions(otherOptions)) { formatKey = NO_FORMAT; } return toString(collection, withHash, deep, seen, otherOptions, formatKey, checkIfMoreToString); } public static <T> String toString(Collection<T> collection, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, String prefix, String delimiter, String suffix, boolean checkIfMoreToString) { if (checkIfMoreToString && collection instanceof MoreToString) { stuffOptionsFromKey(otherOptions, prefix, delimiter, suffix); return ((MoreToString) collection).toString(withHash, deep, seen, otherOptions); } return toString(collection.toArray(), withHash, deep, seen, otherOptions, prefix, delimiter, suffix); } public static <K, V> String toString(Map<K, V> map, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, int formatKey, boolean checkIfMoreToString) { if (checkIfMoreToString && map instanceof MoreToString) { stuffOptionsFromKey(otherOptions, formatKey); return ((MoreToString) map).toString(withHash, deep, seen, otherOptions); } return toString(map.entrySet().toArray(), withHash, deep, seen, otherOptions, formatKey); } public static <K, V> String toString(Map<K, V> map, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = CURLY_BRACES; if (hasFormatOptions(otherOptions)) { formatKey = NO_FORMAT; // if ( checkIfMoreToString && map instanceof MoreToString ) { // return ((MoreToString)map).toString(withHash, deep, seen, // otherOptions); // } // return toString( map.entrySet().toArray(), withHash, deep, // seen, // otherOptions ); } return toString(map, withHash, deep, seen, otherOptions, formatKey, checkIfMoreToString); } public static <K, V> String toString(Map<K, V> map, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, String prefix, String delimiter, String suffix, boolean checkIfMoreToString) { if (checkIfMoreToString && map instanceof MoreToString) { stuffOptionsFromKey(otherOptions, prefix, delimiter, suffix); return ((MoreToString) map).toString(withHash, deep, seen, otherOptions); } return toString(map.entrySet().toArray(), withHash, deep, seen, otherOptions, prefix, delimiter, suffix); } public static <K, V> String toString(Map.Entry<K, V> entry, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, int formatKey, boolean checkIfMoreToString) { if (checkIfMoreToString && entry instanceof MoreToString) { stuffOptionsFromKey(otherOptions, formatKey); return ((MoreToString) entry).toString(withHash, deep, seen, otherOptions); } Pair<K, V> p = new Pair<K, V>(entry.getKey(), entry.getValue()); return toString(p, withHash, deep, seen, otherOptions, formatKey, false); } public static <K, V> String toString(Map.Entry<K, V> entry, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = EQUALS; if (hasFormatOptions(otherOptions)) { formatKey = NO_FORMAT; // if ( checkIfMoreToString && entry instanceof MoreToString ) { // return ((MoreToString)entry).toString(withHash, deep, seen, // otherOptions); // } // Pair< K, V > p = new Pair< K, V >( entry.getKey(), // entry.getValue() ); // return toString( p, withHash, deep, seen, otherOptions, false // ); }// else { return toString(entry, withHash, deep, seen, otherOptions, formatKey, checkIfMoreToString); // } } public static <K, V> String toString(Map.Entry<K, V> entry, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, String prefix, String delimiter, String suffix, boolean checkIfMoreToString) { if (checkIfMoreToString && entry instanceof MoreToString) { stuffOptionsFromKey(otherOptions, prefix, delimiter, suffix); return ((MoreToString) entry).toString(withHash, deep, seen, otherOptions); } Pair<K, V> p = new Pair<K, V>(entry.getKey(), entry.getValue()); return toString(p, withHash, deep, seen, otherOptions, prefix, delimiter, suffix, false); } public static <K, V> String toString(Pair<K, V> pair, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, int formatKey, boolean checkIfMoreToString) { if (checkIfMoreToString && pair instanceof MoreToString) { stuffOptionsFromKey(otherOptions, formatKey); return ((MoreToString) pair).toString(withHash, deep, seen, otherOptions); } return toString(new Object[]{pair.getKey(), pair.getValue()}, withHash, deep, seen, otherOptions, formatKey); } public static <K, V> String toString(Pair<K, V> pair, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, boolean checkIfMoreToString) { int formatKey = hasFormatOptions(otherOptions) ? NO_FORMAT : PARENTHESES; // if ( checkIfMoreToString && pair instanceof MoreToString ) { // return ((MoreToString)pair).toString(withHash, deep, seen, // otherOptions); // } return toString(pair, withHash, deep, seen, otherOptions, formatKey, checkIfMoreToString); } public static <K, V> String toString(Pair<K, V> pair, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, String prefix, String delimiter, String suffix, boolean checkIfMoreToString) { if (checkIfMoreToString && pair instanceof MoreToString) { stuffOptionsFromKey(otherOptions, prefix, delimiter, suffix); return ((MoreToString) pair).toString(withHash, deep, seen, otherOptions); } return toString(new Object[]{pair.getKey(), pair.getValue()}, withHash, deep, seen, otherOptions, prefix, delimiter, suffix); } // public static < T > String toString( final T[] collection, // boolean withHash, boolean deep, // Set< Object > seen, // Map<String,Object> otherOptions ) { // return toString( collection, withHash, deep, seen, otherOptions, true // ); // } /** * @param map * @param key * @param cls * @return a value in map of type T with the given key or null if not * found. */ @SuppressWarnings("unchecked") public static <K, V, T> T get(Map<K, V> map, K key, Class<T> cls) { if (key == null) { return null; } if (map == null) { return null; } V v = map.get(key); if (v == null) { return null; } T t = null; // try { if (cls.isAssignableFrom(v.getClass())) { t = (T) v; } // } catch ( ClassCastException cce ) { // ;// ignore // } return t; } /** * @param map * @param key * @return a value in map of type T with the given key or null if not * found. */ public static <K, V, T> T get(Map<K, V> map, K key) { return get(map, key, null); } private static Map<String, String[]> initFormatOptions() { Map<String, String[]> optionMap = new TreeMap<String, String[]>(); optionMap.put("prefix", prefixes); optionMap.put("suffix", suffixes); optionMap.put("delimiter", delimiters); return optionMap; } private static String[] getOptions(Map<String, Object> options, String[] optionNames) { String[] optionValues = new String[optionNames.length]; if (!Utils2.isNullOrEmpty(options) && !Utils2.isNullOrEmpty(optionNames)) { // initialize option values to null for (int i = 0; i < optionNames.length; ++i) { optionValues[i] = null; } // get matching values for the name for (int i = 0; i < optionNames.length; ++i) { // look for a matching string value first String value = get(options, optionNames[i], String.class); if (value != null) { optionValues[i] = value; } else { // look for a format key integer Integer optionKey = get(options, optionNames[i], Integer.class); if (optionKey != null) { // look up the string value for the format key String[] subOptions = formatOptions.get(optionNames[i]); if (subOptions != null) { if (optionKey > 0 && optionKey < subOptions.length) { value = subOptions[optionKey]; optionValues[i] = value; } } // The format key often applies to all options, so // set any that // are null to the default string value for the name // and key. for (int j = 0; j < optionNames.length; ++j) { // Don't overwrite an assigned value with a // default. if (optionValues[j] != null) { continue; } // look up the string value for the format key subOptions = formatOptions.get(optionNames[j]); if (subOptions != null) { if (optionKey > 0 && optionKey < subOptions.length) { value = subOptions[optionKey]; optionValues[j] = value; } } } } } } } return optionValues; } private static String[] getFormatOptions(Map<String, Object> options) { // final Object[] objects = formatOptions.keySet().toArray(); String[] optionNames = new String[formatOptions.size()]; Utils2.toArrayOfType(formatOptions.keySet(), optionNames, String.class); // new String[] { "prefix", "delimiter", "suffix" }; return getOptions(options, optionNames); } public static boolean hasFormatOptions(Map<String, Object> options) { return (options != null && options.get("prefix") != null); } /** * Writes an array to a string with MoreToString options, including an * explicit format key. For example, if the format key * MoreToString.PARENTHESES is given, the collection will be enclosed in * parentheses, "(" and ")" and the default comma will be used as a * delimiter between elements. * * @param withHash whether to include "@" + hasCode() in the returned String. * @param deep whether to include member/child detail and call their * MoreToString.toString() (typically) with the same options. * @param seen whether the object has already been written with * deep=true, in which case it will set deep=false to end the * recursion. * @param otherOptions other class or context-specific options with names and * values. * @param formatKey a MoreToString constant indicating the grouping format of * the elements of the collection, for example, * MoreToString.CURLY_BRACES. * @return the string representation of the object. */ public static <T> String toString(final T[] array, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, int formatKey) { if (otherOptions == null) { otherOptions = new TreeMap<String, Object>(); } stuffOptionsFromKey(otherOptions, formatKey); return toString(array, withHash, deep, seen, otherOptions); } public static void stuffOptionsFromKey(Map<String, Object> options, int formatKey) { if (formatKey == NO_FORMAT) { return; } if (options == null) { return; } for (String opt : formatOptions.keySet()) { options.put("prefix", formatKey); } } private static void stuffOptionsFromKey(Map<String, Object> options, String prefix, String delimiter, String suffix) { if (options == null) { options = new TreeMap<String, Object>(); } options.put("prefix", prefix); options.put("delimiter", delimiter); options.put("suffix", suffix); } public static void removeFormatOptions(Map<String, Object> options) { if (Utils2.isNullOrEmpty(options)) { return; } for (String opt : formatOptions.keySet()) { options.remove(opt); } } // public static < T > String toShortString( final T[] array, int // formatKey, // boolean checkIfMoreToString ) { // String[] options = getFormatOptions(otherOptions); // String delimiter = options[0]; // ordered by option name // String prefix = options[1]; // String suffix = options[2]; // return null; // } public static String toString(final int[] array) { if (array == null) { return "null"; } Integer[] arrayI = new Integer[array.length]; for (int i = 0; i < array.length; ++i) { arrayI[i] = array[i]; } return toString(arrayI); } public static String toString(final double[] array) { if (array == null) { return "null"; } Double[] arrayI = new Double[array.length]; for (int i = 0; i < array.length; ++i) { arrayI[i] = array[i]; } return toString(arrayI); } public static String toString(final boolean[] array) { if (array == null) { return "null"; } Boolean[] arrayI = new Boolean[array.length]; for (int i = 0; i < array.length; ++i) { arrayI[i] = array[i]; } return toString(arrayI); } // TODO -- need methods like above for other primitives public static <T> String toString(final T[] array) { return toString(array, false, false, null, null); } public static <T> String toString(final T[] array, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions) { String[] options = getFormatOptions(otherOptions); String delimiter = options[0]; // ordered by option name String prefix = options[1]; String suffix = options[2]; if (prefix != null && !otherOptions.containsKey("formatDeep")) { removeFormatOptions(otherOptions); } return toString(array, withHash, deep, seen, otherOptions, prefix, delimiter, suffix); } public static <T> String toString(final T[] array, boolean withHash, boolean deep, Set<Object> seen, Map<String, Object> otherOptions, String prefix, String delimiter, String suffix) { StringBuffer sb = new StringBuffer(); if (prefix == null) { prefix = "["; suffix = "]"; } if (prefix.equals("{") && !suffix.equals("}")) { suffix = "}"; } if (delimiter == null) { delimiter = ", "; } sb.append(prefix); boolean first = true; for (Object object : array) { if (first) { first = false; } else { sb.append(delimiter); } if (deep && (seen == null || !seen.contains(object))) { sb.append(MoreToString.Helper.toString(object, withHash, deep, seen, otherOptions)); } else { sb.append(MoreToString.Helper.toShortString(object)); } } sb.append(suffix); return sb.toString(); } public static List<String> fromString(String s, String prefix, String delimiter, String suffix) { List<String> list = Collections.emptyList(); Pattern p = Pattern.compile(prefix); Matcher matcher = p.matcher(s); if (!matcher.find()) { return list; } p = Pattern.compile(delimiter); String[] arr = p.split(s.substring(matcher.end())); String last = arr[arr.length - 1]; p = Pattern.compile(suffix); matcher = p.matcher(last); int startPos = -1; while (matcher.find()) { startPos = matcher.start(); } int length = arr.length; if (startPos >= 0) { if (startPos == 0) { --length; } else { arr[length - 1] = last.substring(0, startPos); } } list = Arrays.asList(arr).subList(0, length); return list; } public static void fromString(Map<String, String> map, String s) { fromString(map, s, "[\\[{(]\\s*", ",\\s*", "\\s*[\\]})]", "[\\[{(]\\s*", "\\s*=\\s*", "\\s*[\\]})]"); } public static void fromString(Map<String, String> map, String s, String prefix, String delimiter, String suffix, String keyValuePrefix, String keyValueDelimiter, String keyValueSuffix) { if (map == null) { map = new HashMap<String, String>(); } map.clear(); Pattern p = Pattern.compile(prefix); Matcher matcher = p.matcher(s); if (!matcher.find()) { return; } int start = matcher.end(); Pattern d = Pattern.compile(delimiter); Pattern kvp = Pattern.compile(keyValuePrefix); Pattern kvd = Pattern.compile(keyValueDelimiter); Pattern kvs = Pattern.compile(keyValueSuffix); boolean gotDelimiter = true; while (gotDelimiter) { Debug.outln("\nstart = " + start); Debug.outln("substring = " + s.substring(start)); // find key-value prefix matcher = kvp.matcher(s.substring(start)); if (!matcher.find()) { break; } start = start + matcher.end(); Debug.outln("\nkvp match = " + matcher.group()); Debug.outln("matcher.start() = " + matcher.start()); Debug.outln("matcher.end() = " + matcher.end()); Debug.outln("\nstart = " + start); Debug.outln("substring = " + s.substring(start)); // find delimiter between key and value matcher = kvd.matcher(s.substring(start)); if (!matcher.find()) { break; } // get the key as the characters before the key-value delimiter String key = s.substring(start, start + matcher.start()); start = start + matcher.end(); Debug.outln("\nkvd match = " + matcher.group()); Debug.outln("matcher.start() = " + matcher.start()); Debug.outln("matcher.end() = " + matcher.end()); Debug.outln("\nkey = " + key); Debug.outln("\nstart = " + start); Debug.outln("substring = " + s.substring(start)); // get the value between the key-value delimiter and the // key-value suffix matcher = kvs.matcher(s.substring(start)); if (!matcher.find()) { break; } String value = s.substring(start, start + matcher.start()); start = start + matcher.end(); boolean foundValue = matcher.start() != 0; Debug.outln("\nkvs match = " + matcher.group()); Debug.outln("matcher.start() = " + matcher.start()); Debug.outln("matcher.end() = " + matcher.end()); if (foundValue) { Debug.outln("\nvalue = " + value); } Debug.outln("\nstart = " + start); Debug.outln("substring = " + s.substring(start)); if (foundValue) { // add the key-value pair to the map map.put(key, value); } // skip over the delimiter matcher = d.matcher(s.substring(start)); // set the start position at the end of the delimiter to get the // next pair gotDelimiter = matcher.find(); if (!foundValue) { if (gotDelimiter) { foundValue = matcher.start() != 0; if (!foundValue) { System.err.println("Error! fromString(): no value parsed from " + s.substring(start)); } value = s.substring(start, start + matcher.start()); } else { value = s.substring(start); } Debug.outln("\nvalue = " + value); map.put(key, value); } if (gotDelimiter) { start = start + matcher.end(); Debug.outln("\nd match = " + matcher.group()); Debug.outln("matcher.start() = " + matcher.start()); Debug.outln("matcher.end() = " + matcher.end()); Debug.outln("\nstart = " + start); Debug.outln("substring = " + s.substring(start)); } } Debug.outln("parsed map = " + map); } private static String readLine(String format, Object... args) { if (System.console() != null) { return System.console().readLine(format, args); } System.out.print(String.format(format, args)); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); try { return reader.readLine(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } private static void print(String format, Object... args) { if (System.console() != null) { System.console().format(format, args); } else { System.out.format(format, args); } } private static void println(String format, Object... args) { print(format + "%n", args); } public static class TestMoreToString { /** * testing fromString() for a Map< String, String > with numerical * data */ @Test public void testFromString() { // create a map of numbers Map<Integer, Double> mapTruth = new TreeMap<Integer, Double>(); mapTruth.put(0, 2.31359797761508); mapTruth.put(84600, 2.080913063156552); mapTruth.put(85500, 2.3810581138436953); // Capture the mapTruth toString() for parsing with fromString() // later. // TODO -- this doesn't match parser. fromString expects // parentheses // around the key=value pairs, but toString() leaves them out. // TODO -- fromString() also can't tolerate spaces, which // toString() inserts. // String mapString = mapTruth.toString(); // String mapString = // "{(0=2.31359797761508),(84600=2.080913063156552),(85500=2.3810581138436953)}"; String mapString = mapTruth.toString().replaceAll("([.\\w-]*)=([.\\w-]*)", "($1=$2)") .replaceAll(" ", ""); // convert the map into a Map<String, String> by calling // toString() on // each key and value Map<String, String> mapStringTruth = new TreeMap<String, String>(); for (Entry<Integer, Double> e : mapTruth.entrySet()) { mapStringTruth.put(e.getKey().toString(), e.getValue().toString()); } // parse mapString as a String map Map<String, String> mapTest = new TreeMap<String, String>(); fromString(mapTest, mapString); System.out.println("string to parse into map: " + mapString); System.out.println("map after parsing:\n"); for (Map.Entry<String, String> e : mapTest.entrySet()) { System.out.println("key = " + e.getKey()); System.out.println("value = " + e.getValue()); } Assert.assertArrayEquals(mapTest.keySet().toArray(), mapStringTruth.keySet().toArray()); Assert.assertArrayEquals(mapTest.values().toArray(), mapStringTruth.values().toArray()); Assert.assertEquals(mapTest.toString(), mapStringTruth.toString()); Assert.assertEquals(mapTest.toString(), mapTruth.toString()); Assert.assertEquals(mapTest, mapStringTruth); System.out.println("\nbye!"); } } public static void main(String[] args) { // specific regex test String input = "0,1\\n1,2\\n"; Pattern p = Pattern.compile("\\s*,\\s*"); Matcher m = p.matcher(input); while (m.find()) { print("Start index: " + m.start()); print(" End index: " + m.end() + " "); println(m.group()); } // repeated user tests of regex from console input while (true) { String patternString = readLine("enter pattern:"); if (patternString == null) { break; } Pattern pattern = Pattern.compile(patternString); String string = readLine("enter string to match against pattern:"); if (string == null) { break; } Matcher matcher = pattern.matcher(string); while (matcher.find()) { print("Start index: " + matcher.start()); print(" End index: " + matcher.end() + " "); println(matcher.group()); } } // testing fromString() for a map } } }