From 42cbd8237323aafd856e2f06882099b3f268a4c2 Mon Sep 17 00:00:00 2001
From: JamesStauffer <jstauffer@spscommerce.com>
Date: Tue, 21 May 2013 10:51:15 -0500
Subject: [PATCH 2/2] Added new conversion rule that changes to parameterized
 log format messages.

---
 .../slf4j/migrator/line/FormatConversionRule.java  | 100 +++++++++++++++++++++
 .../java/org/slf4j/migrator/line/Log4jRuleSet.java |   3 +
 .../org/slf4j/migrator/line/Log4jRuleSetTest.java  |  13 +++
 3 files changed, 116 insertions(+)
 create mode 100644 slf4j-migrator/src/main/java/org/slf4j/migrator/line/FormatConversionRule.java

diff --git a/slf4j-migrator/src/main/java/org/slf4j/migrator/line/FormatConversionRule.java b/slf4j-migrator/src/main/java/org/slf4j/migrator/line/FormatConversionRule.java
new file mode 100644
index 0000000..7c6d038
--- /dev/null
+++ b/slf4j-migrator/src/main/java/org/slf4j/migrator/line/FormatConversionRule.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2004-2011 QOS.ch
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free  of charge, to any person obtaining
+ * a  copy  of this  software  and  associated  documentation files  (the
+ * "Software"), to  deal in  the Software without  restriction, including
+ * without limitation  the rights to  use, copy, modify,  merge, publish,
+ * distribute,  sublicense, and/or sell  copies of  the Software,  and to
+ * permit persons to whom the Software  is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The  above  copyright  notice  and  this permission  notice  shall  be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
+ * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
+ * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+package org.slf4j.migrator.line;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class represents a conversion rule that makes use of a parameterized
+ * format message that avoids unnecessary String concatination in log messages
+ * 
+ * @author James Stauffer
+ */
+public class FormatConversionRule implements ConversionRule {
+
+  final private Pattern pattern;
+  final private Pattern concatPatternStart;
+  final private Pattern concatPatternEndMiddle;//will have another string after
+  final private Pattern concatPatternEndFinal;//Final variable in concat String
+  final private Pattern logEndPattern;
+  final private Pattern logThrowablePattern;
+  
+  public FormatConversionRule(Pattern pattern) {
+    this.pattern = pattern;
+    concatPatternStart = Pattern.compile("\"\\s*\\+\\s*");
+    concatPatternEndMiddle = Pattern.compile("\\s*\\+\\s*\"");
+    concatPatternEndFinal = Pattern.compile("\\s*\\)\\s*;");
+    logThrowablePattern = Pattern.compile("\\s*,\\s*\\w+\\s\\)\\s*;");
+    logEndPattern = Pattern.compile("\\s*\\)\\s*;\\s*");
+  }
+
+  /* (non-Javadoc)
+   * @see org.slf4j.converter.ConversionRule#getReplacement(String)
+   */
+  public String[] getReplacement(String originalText) {
+    Matcher matcher = pattern.matcher(originalText);
+    if(matcher.find()) {
+        StringBuilder replacementLine = new StringBuilder(originalText.substring(0, matcher.end()));
+        String text = originalText.substring(matcher.end());
+        List<String> concatObjects = new ArrayList<String>();
+        for(matcher = concatPatternStart.matcher(text); matcher.find(); matcher = concatPatternStart.matcher(text)) {
+            replacementLine.append(text.substring(0, matcher.start()));//Keep everything up to the concat
+            Matcher matcherEndMiddle = concatPatternEndMiddle.matcher(text);
+            if(matcherEndMiddle.find()) {
+                concatObjects.add(text.substring(matcher.end(), matcherEndMiddle.start()));
+                replacementLine.append("{}");
+                text = text.substring(matcherEndMiddle.end());
+            } else {
+                Matcher matcherEndfinal = concatPatternEndFinal.matcher(text);
+                if(matcherEndfinal.find()) {
+                    concatObjects.add(text.substring(matcher.end(), matcherEndfinal.start()));
+                    replacementLine.append("{}");
+                    text = "\");" + text.substring(matcherEndfinal.end());
+                    break;
+                } else {
+                    return new String[]{originalText + "//TODO SLF4J Migrator unable to automatically change to parameterized logging"};
+                }
+                
+            }
+        }
+        Matcher endMatcher = logEndPattern.matcher(text);
+        if(endMatcher.find()) {
+            replacementLine.append(text.substring(0, endMatcher.start()));
+            for(String concatObject : concatObjects) {
+                replacementLine.append(", ").append(concatObject);
+            }
+            replacementLine.append(");");
+            return new String[]{replacementLine.toString()};
+        } else {//Didn't find end of log call so probably wrapped to multiple lines
+            return new String[]{originalText + "//TODO SLF4J Migrator unable to automatically change to parameterized logging because it appears the line wraps"};
+        }
+    } else {
+        return new String[]{originalText};
+    }
+  }
+}
diff --git a/slf4j-migrator/src/main/java/org/slf4j/migrator/line/Log4jRuleSet.java b/slf4j-migrator/src/main/java/org/slf4j/migrator/line/Log4jRuleSet.java
index 9112463..ccca69b 100644
--- a/slf4j-migrator/src/main/java/org/slf4j/migrator/line/Log4jRuleSet.java
+++ b/slf4j-migrator/src/main/java/org/slf4j/migrator/line/Log4jRuleSet.java
@@ -61,6 +61,8 @@ public class Log4jRuleSet implements RuleSet {
     SingleConversionRule crFactory1 = new SingleConversionRule(Pattern
         .compile("LogManager.getLogger\\("), "LoggerFactory.getLogger(");
 
+    FormatConversionRule fc0 = new FormatConversionRule(Pattern
+        .compile("log(log)?\\.(trace)|(debug)|(info)|(warn)|(error)|(fatal)\\(\""));
     conversionRuleList = new ArrayList<ConversionRule>();
     conversionRuleList.add(crImport0);
     conversionRuleList.add(crImport1);
@@ -68,6 +70,7 @@ public class Log4jRuleSet implements RuleSet {
     conversionRuleList.add(crImportMDC);
     conversionRuleList.add(crFactory0);
     conversionRuleList.add(crFactory1);
+    conversionRuleList.add(fc0);
   }
 
   public Iterator<ConversionRule> iterator() {
diff --git a/slf4j-migrator/src/test/java/org/slf4j/migrator/line/Log4jRuleSetTest.java b/slf4j-migrator/src/test/java/org/slf4j/migrator/line/Log4jRuleSetTest.java
index 7fba8fe..2d86e0b 100644
--- a/slf4j-migrator/src/test/java/org/slf4j/migrator/line/Log4jRuleSetTest.java
+++ b/slf4j-migrator/src/test/java/org/slf4j/migrator/line/Log4jRuleSetTest.java
@@ -156,4 +156,17 @@ public class Log4jRuleSetTest extends TestCase {
    log4jConverter
    .getOneLineReplacement(" = LogManager.getLogger(MyComponent.class);"));
    }
+
+  public void testParamertizedReplacement()  {
+    assertEquals("Debug call with one variable", "log.debug(\"blah={}\", blah);", log4jConverter
+      .getOneLineReplacement("log.debug(\"blah=\" + blah);"));
+    assertEquals("info call with two variables", "log.info(\"foo={} bar={}\", foo, bar);", log4jConverter
+      .getOneLineReplacement("log.info(\"foo=\" + foo + \" bar=\" + bar);"));
+    assertEquals("wrapped line", "log.debug(\"foo=\" + foo//TODO SLF4J Migrator unable to automatically change to parameterized logging", log4jConverter
+      .getOneLineReplacement("log.debug(\"foo=\" + foo"));
+    assertEquals("warn call with one variable and throwable", "log.warn(\"foo={}\", foo, e);", log4jConverter
+      .getOneLineReplacement("log.warn(\"foo=\" + foo, e);"));
+    assertEquals("error call with two variables and throwable", "log.warn(\"foo={} bar={}\", foo, bar, e);", log4jConverter
+      .getOneLineReplacement("log.warn(\"foo=\" + foo + \" bar=\" + bar, e);"));
+  }
 }
-- 
1.8.1.msysgit.1

