MessageFormat.format() with named parameters

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
5
down vote

favorite












I was looking for how to use MessageFormat.format() in Java with named parameters, as a similar option exists in Python, and found this question. Some functions are presented there, although they use internal cycles and StringBuilder, and apparently they can fall into infinite loops (comments there suggest that, and it makes sense).



Option 1 (potential infinite loop):




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();




Option 2 (infinite loop removed):




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());




And the question is, why not something simpler? Why is there an internal cycle?
Here my option (which probably has some issue I'm not aware of).



public static String format(String str, Map<String, String> values) str.isEmpty()) 
return finalString;

// validate parameters
else if (values == null






share|improve this question



























    up vote
    5
    down vote

    favorite












    I was looking for how to use MessageFormat.format() in Java with named parameters, as a similar option exists in Python, and found this question. Some functions are presented there, although they use internal cycles and StringBuilder, and apparently they can fall into infinite loops (comments there suggest that, and it makes sense).



    Option 1 (potential infinite loop):




    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();




    Option 2 (infinite loop removed):




    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());




    And the question is, why not something simpler? Why is there an internal cycle?
    Here my option (which probably has some issue I'm not aware of).



    public static String format(String str, Map<String, String> values) str.isEmpty()) 
    return finalString;

    // validate parameters
    else if (values == null






    share|improve this question























      up vote
      5
      down vote

      favorite









      up vote
      5
      down vote

      favorite











      I was looking for how to use MessageFormat.format() in Java with named parameters, as a similar option exists in Python, and found this question. Some functions are presented there, although they use internal cycles and StringBuilder, and apparently they can fall into infinite loops (comments there suggest that, and it makes sense).



      Option 1 (potential infinite loop):




      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();




      Option 2 (infinite loop removed):




      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());




      And the question is, why not something simpler? Why is there an internal cycle?
      Here my option (which probably has some issue I'm not aware of).



      public static String format(String str, Map<String, String> values) str.isEmpty()) 
      return finalString;

      // validate parameters
      else if (values == null






      share|improve this question













      I was looking for how to use MessageFormat.format() in Java with named parameters, as a similar option exists in Python, and found this question. Some functions are presented there, although they use internal cycles and StringBuilder, and apparently they can fall into infinite loops (comments there suggest that, and it makes sense).



      Option 1 (potential infinite loop):




      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();




      Option 2 (infinite loop removed):




      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());




      And the question is, why not something simpler? Why is there an internal cycle?
      Here my option (which probably has some issue I'm not aware of).



      public static String format(String str, Map<String, String> values) str.isEmpty()) 
      return finalString;

      // validate parameters
      else if (values == null








      share|improve this question












      share|improve this question




      share|improve this question








      edited May 15 at 15:39









      Sam Onela

      5,76961543




      5,76961543









      asked May 15 at 11:05









      Fernando César S.

      262




      262




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          2
          down vote













          Performing string substitutions using multiple passes is almost always the wrong approach, and leads to bugs. If one of the values happens to be a string that looks like a %(key), then all sorts of unpredictable things could happen, including various uncontrolled format string attacks!



          Therefore, the string replacements must be done in a single pass of the format string. I recommend doing it using a regular expression.



          Furthermore, your method provides no escape mechanism, in case you need to specify a literal %(blah) in the format string. In Java, it would be customary to use backslash as an escape character.



          Suggested solution



          This solution uses Matcher.replaceAll(Function<MatchResult,String> replacer), which was introduced in Java 9, to provide each substitution text via a callback.



          import java.util.Map;
          import java.util.regex.Matcher;
          import java.util.regex.Pattern;

          public class NamedFormatter
          private static final Pattern RE = Pattern.compile(
          "\\(.)" + // Treat any character after a backslash literally
          "





          share|improve this answer























            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194446%2fmessageformat-format-with-named-parameters%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            2
            down vote













            Performing string substitutions using multiple passes is almost always the wrong approach, and leads to bugs. If one of the values happens to be a string that looks like a %(key), then all sorts of unpredictable things could happen, including various uncontrolled format string attacks!



            Therefore, the string replacements must be done in a single pass of the format string. I recommend doing it using a regular expression.



            Furthermore, your method provides no escape mechanism, in case you need to specify a literal %(blah) in the format string. In Java, it would be customary to use backslash as an escape character.



            Suggested solution



            This solution uses Matcher.replaceAll(Function<MatchResult,String> replacer), which was introduced in Java 9, to provide each substitution text via a callback.



            import java.util.Map;
            import java.util.regex.Matcher;
            import java.util.regex.Pattern;

            public class NamedFormatter
            private static final Pattern RE = Pattern.compile(
            "\\(.)" + // Treat any character after a backslash literally
            "





            share|improve this answer



























              up vote
              2
              down vote













              Performing string substitutions using multiple passes is almost always the wrong approach, and leads to bugs. If one of the values happens to be a string that looks like a %(key), then all sorts of unpredictable things could happen, including various uncontrolled format string attacks!



              Therefore, the string replacements must be done in a single pass of the format string. I recommend doing it using a regular expression.



              Furthermore, your method provides no escape mechanism, in case you need to specify a literal %(blah) in the format string. In Java, it would be customary to use backslash as an escape character.



              Suggested solution



              This solution uses Matcher.replaceAll(Function<MatchResult,String> replacer), which was introduced in Java 9, to provide each substitution text via a callback.



              import java.util.Map;
              import java.util.regex.Matcher;
              import java.util.regex.Pattern;

              public class NamedFormatter
              private static final Pattern RE = Pattern.compile(
              "\\(.)" + // Treat any character after a backslash literally
              "





              share|improve this answer

























                up vote
                2
                down vote










                up vote
                2
                down vote









                Performing string substitutions using multiple passes is almost always the wrong approach, and leads to bugs. If one of the values happens to be a string that looks like a %(key), then all sorts of unpredictable things could happen, including various uncontrolled format string attacks!



                Therefore, the string replacements must be done in a single pass of the format string. I recommend doing it using a regular expression.



                Furthermore, your method provides no escape mechanism, in case you need to specify a literal %(blah) in the format string. In Java, it would be customary to use backslash as an escape character.



                Suggested solution



                This solution uses Matcher.replaceAll(Function<MatchResult,String> replacer), which was introduced in Java 9, to provide each substitution text via a callback.



                import java.util.Map;
                import java.util.regex.Matcher;
                import java.util.regex.Pattern;

                public class NamedFormatter
                private static final Pattern RE = Pattern.compile(
                "\\(.)" + // Treat any character after a backslash literally
                "





                share|improve this answer















                Performing string substitutions using multiple passes is almost always the wrong approach, and leads to bugs. If one of the values happens to be a string that looks like a %(key), then all sorts of unpredictable things could happen, including various uncontrolled format string attacks!



                Therefore, the string replacements must be done in a single pass of the format string. I recommend doing it using a regular expression.



                Furthermore, your method provides no escape mechanism, in case you need to specify a literal %(blah) in the format string. In Java, it would be customary to use backslash as an escape character.



                Suggested solution



                This solution uses Matcher.replaceAll(Function<MatchResult,String> replacer), which was introduced in Java 9, to provide each substitution text via a callback.



                import java.util.Map;
                import java.util.regex.Matcher;
                import java.util.regex.Pattern;

                public class NamedFormatter
                private static final Pattern RE = Pattern.compile(
                "\\(.)" + // Treat any character after a backslash literally
                "






                share|improve this answer















                share|improve this answer



                share|improve this answer








                edited May 15 at 14:15


























                answered May 15 at 13:24









                200_success

                123k14143399




                123k14143399






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194446%2fmessageformat-format-with-named-parameters%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Python Lists

                    Aion

                    JavaScript Array Iteration Methods