views:

786

answers:

5

Hello!

In Python, when formatting string, I can fill placeholders by name rather than by position, like that:

print "There's an incorrect value '%(value)s' in column # %(column)d" % \
  { 'value': x, 'column': y }

I wonder if that is possible in Java (hopefully, without external libraries)? Thanks in advance.

A: 

Try Freemarker, templating library.

alt text

Boris Pavlović
Freemarker? i guess he is willing to know, how to do this in plain java. Anyways if Freemarker is the probable answer then can i say JSP too will be the correct answer?
Rakesh Juyal
Thanks, but for my task at hand this seems to be kind of overkill. But thanks.
Andy
@Rakesh JSP is a very "view/FE" specific thing. I have used FreeMarker in the past for generating XML and sometimes even generated JAVA files. Andy am afraid you will have to write one utility yourself (or like the one prescribed above)
Calm Storm
+1  A: 

not quite, but you can use MessageFormat to reference one value multiple times:

MessageeFormat.format("There's an incorrect value \"{0}\" in column # {1}", x, y);

The above can be done with String.format() as well, but I find messageFormat syntax cleaner if you need to build complex expressions, plus you dont need to care about the type of the object you are putting into the string

Thanks, but the point is I cannot position parameters.
Andy
not sure why you can't, the position in the string is not important, only the position in the list of args, which makes it a renaming problem. You know the name of the keys, which means you can decide a position for a key in the list of arguments. from now on value will be known as 0 and column as 1: MessageeFormat.format("There's an incorrect value \"{0}\" in column # {1}, using {0} as value can cause many problems", valueMap.get('value'), valueMap.get('column'));
Thanks for a clue, it helped me to write simple function that does exactly what I want (I've put it below).
Andy
A: 

You could have something like this on a string helper class

/**
     * An interpreter for strings with named placeholders.
     *
     * For example given the string "hello %(myName)" and the map <code>
     *      <p>Map<String, Object> map = new HashMap<String, Object>();</p>
     *      <p>map.put("myName", "world");</p>
     * </code>
     *
     * the call {@code format("hello %(myName)", map)} returns "hello world"
     *
     * It replaces every occurrence of a named placeholder with its given value
     * in the map. If there is a named place holder which is not found in the
     * map then the string will retain that placeholder. Likewise, if there is
     * an entry in the map that does not have its respective placeholder, it is
     * ignored.
     *
     * @param str
     *            string to format
     * @param values
     *            to replace
     * @return formatted string
     */
    public static String format(String str, Map<String, Object> values) {

        StringBuilder builder = new StringBuilder(str);

        for (Entry<String, Object> entry : values.entrySet()) {

            int start;
            String pattern = "%(" + entry.getKey() + ")";
            String value = entry.getValue().toString();

            // Replace every occurence of %(key) with value
            while ((start = builder.indexOf(pattern)) != -1) {
                builder.replace(start, start + pattern.length(), value);
            }
        }

        return builder.toString();
    }
Lombo
Thanks a lot, it does almost what I want, but the only thing is it does not account modifiers (consider "%(key)08d")
Andy
A: 

Thanks for all your help! Using all your clues, I've written routine to do exactly what I want -- python-like string formatting using dictionary. Since I'm Java newbie, any hints are appreciated.

  public static String dictFormat(String format, Hashtable<String, Object> values)
  {
    StringBuilder convFormat = new StringBuilder(format);
    Enumeration<String> keys = values.keys();
    ArrayList valueList = new ArrayList();
    int currentPos = 1;
    while (keys.hasMoreElements())
      {
        String key = keys.nextElement(),
          formatKey = "%(" + key + ")",
          formatPos = "%" + Integer.toString(currentPos) + "$";
        int index = -1;
        while ((index = convFormat.indexOf(formatKey, index)) != -1)
          {
            convFormat.replace(index, index + formatKey.length(), formatPos);
            index += formatPos.length();
          }
        valueList.add(values.get(key));
        ++currentPos;
      }
    return String.format(convFormat.toString(), valueList.toArray());
  }
Andy
A: 

You can use StringTemplate library, it offers what you want and much more on the top of that: http://www.antlr.org/wiki/display/ST/Introduction

Shooshpanchick