Skip to main content

Replacing $ in a String in Java

I faced an issue in my project recently where we read a password for an admin user first from a file which has a dummy or wrong password written in it. Then I read the password from database and replace the dummy password with the real password that I get from Database. So the code was something very simple like this (this is actually different from originalk).

String myXmlDoc = readALargeXmlAsString();
String realPassword = readFromDatabase();
String password = myXmlDoc.replaceAll("dummyPassword", realPassword);

All on a sudden we found that this replacement started failing after the password of that admin user was changed recently. Here is the exception log.

Exception in thread "main" java.lang.IllegalArgumentException: Illegal group reference
at java.util.regex.Matcher.appendReplacement(Matcher.java:706)
at java.util.regex.Matcher.replaceAll(Matcher.java:806)
at java.lang.String.replaceAll(String.java:2000)
at com.salesforce.test.StringReplaceTest.main(StringReplaceTest.java:34)

So I figured out from the log that it’s actually the replaceAll() pattern matching method that is causing the failure when you have $ in your replacement string (not in the pattern itself). So as per the first code example, if you had a $ character as one of the characters for realPassword, then it would throw the exception above. While the solution was to use a different admin for now who doesn’t have a password containing $ sign, I sat down to investigate it in detail and write a long term solution.
First I reproduced the issue in 3 different ways. Here is a code that will tell you depending on where you are putting the $ sign, the exception stack trace will be different. I give you the code and the compile and run instructions so that you can test it yourself.

package com.salesforce.test;

/**
* To compiple: javac -d . StringReplaceTest.java
* To run: java com.salesforce.test.StringReplaceTest
* or, java com.salesforce.test.StringReplaceTest start
* or, java com.salesforce.test.StringReplaceTest middle
* or, java com.salesforce.test.StringReplaceTest end
* @author ashik
*/
public class StringReplaceTest {
public static void main(String[] args) {
System.out.println("\nStringReplaceTest starts.....\n");
String firstStr = "I am a Java programmer working in USA. Chess is my hobby and here in USA lot of people play chess.";
System.out.println("firstStr before replacing = " + firstStr);
String positionPOfDollarSign = "";
if(args.length > 0) {
positionPOfDollarSign = args[0];
}
String secondStr = "";
if("start".equalsIgnoreCase(positionPOfDollarSign)) {
secondStr = firstStr.replaceAll("USA", "$PUT_A_VALUE123"); // illegal group reference
} else if("middle".equalsIgnoreCase(positionPOfDollarSign)) {
secondStr = firstStr.replaceAll("USA", "PUT_A_VALUE$123"); // String index out of range: 15
} else if("end".equalsIgnoreCase(positionPOfDollarSign)) {
secondStr = firstStr.replaceAll("USA", "PUT_A_VALUE123$"); // java.lang.IndexOutOfBoundsException: No group 1
} else {
secondStr = firstStr.replaceAll("USA", "PUT_A_VALUE123"); // no error
}
System.out.println("\nsecondStr after replacing firstStr = " + secondStr);
System.out.println("\nStringReplaceTest ends.....\n");
}
}

Developing the fix was a little tricky as replacing the $ and \ characters (these 2 are what makes trouble) using regular methods like Spring.split() or StringTokenizer class doesn’t work as those itself can’t process $ correctly. So I had to do my search and replace based on the String.indexOf() and String.substring(). Here is my fix and I would like to know your feedback on this. Please note that Apache StringUtils will be a very good resource to use here instead of trying to write the algorithm yourself.

package com.google.test;

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

/**
 * To compiple: javac -d . SfdcReplaceSubstring3.java
 * To run: java com.google.test.SfdcReplaceSubstring3
 *
 * @author ashik
 */
public final class SfdcReplaceSubstring3 {

 // private static String firstStr = "I am a Java programmer and a $Chess$ player working in USA. $Chess$ is my hobby and here in USA lot of people play $Chess$. USA had a great Chess player named Bobby Fischer.";

 private static String firstStr = "Java, Apex, $Chess$ and Oracle - which one do you like? I guess Chess. If not $Chess$ then what else?";

 private static String patternToSearch = "$Chess$";

 private static String[] replacementStrFromDB = { null, "", " ", "PUT_A_VALUE123",
   "PUT#A^VALUE!123?a+b/c>d", "PUT$A$VALUE123", "$PUT_A_VALUE123",
   "PUT_A_$VALUE123", "PUT_A_VALUE123$", "PUT_A_VALUE$123",
   "PUT_A_VALUE123$", "\\$PUT_A_VALUE123", "\\\\$PUT_A_VALUE123",
   "\\PUT_A_VALUE123", "\\\\PUT_A_VALUE123", "\\PUT_A_VALUE$123",
   "\\\\PUT_A_VALUE$123", "\\PUT_A_VALUE123$", "\\\\PUT_A_VALUE123$",
   "$PUT_A_VALUE$123$" };

 public static void main(String[] args) {
  System.out.println("\nfirstStr before replacing = " + firstStr);
  System.out.println("\npatternToSearch = " + patternToSearch);
  // System.out.println("Direct replacement: " +
  // matcher.replaceAll("PUT\\$A\\$VALUE123"));
  for (int i = 0; i < replacementStrFromDB.length; i++)
   try {
    System.out.println("\nExecuted test#"
      + (i + 1)
      + ": "
      + "firstStr after replacing with "
      + replacementStrFromDB[i]
      + " = "
      + replace(firstStr, patternToSearch,
        replacementStrFromDB[i]));
   } catch (Exception e) {
    System.out.println("\nExecuted test#" + (i + 1) + ": "
      + "Exception while replacing firstStr with "
      + replacementStrFromDB[i] + " = " + e.getMessage());
   }
 }

 public static String replace(String text, String searchString,
   String replacement) {
  int start = 0;
  int end = text.indexOf(searchString, start);
  if (end == -1) {
   return text;
  }
  int replLength = searchString.length();
  StringBuilder buf = new StringBuilder();
  while (end != -1) {
   buf.append(text.substring(start, end)).append(replacement);
   start = end + replLength;
   end = text.indexOf(searchString, start);
  }
  buf.append(text.substring(start));
  return buf.toString();
 }

}

Comments

Popular posts from this blog

Moved To New House & Tuhin Bhai's Quick Visit

We moved to a new rental house near BART end of last month. We have stayed in LakeView Apartments for three and half years before moving here. I was too busy with my office works the last month and hence Shusmita did most of the packings. We got the new house key 1 week earlier than our move in date of first November. So we actually moved 24th October Saturday and took rest of the one week to slowly finish the moves. We rented DesiMovers who moved all out packed cartoon (30+) and many unpacked staffs. They disassembled the bed and assembled back again. Although a bit costly, they were quick and did a good job of transferring all the staffs and assembling those back again. All our friends in Fremont liked the house as we did. I wish I would not be a tenant but the owner of the house! However, buying house is very expensive in California and I can't simply afford it maintaining my current lifestyle, I let myself forget about it. Tuhin Bhai (Ripa's hubby) gave a surprise visit...

Bought New Car: Honda Accord 2009 LX-P Maroon Color

After a month of my Civic being completely destroyed in a car accident, I bought my new car yesterday. It's Honda Accord 2009 LX-P Maroon Color that I bought from Autowest Honda of Fremont . I have shopped around for car in Toyota and Honda. While Toyota Camry 2010 is also a very good car, somehow we ended up liking Accord. Whlile in Accord you feel the road and the power behind the car more, for Camry it's the smoothness of driving experience. Shusmita didn't want to even go for a test drive of Hyundai Sonata although that is also a comparable car at a cheaper price. I guess we are so used to our Honda Civic that only another Honda car would have been able to psychologically match our unknown requirements. I gave a four thousand dollars down payment and the rest my dealer financed me from American Honda Finance. While buying the car, I also bought the 6 years service, mechanical breakdown warranty, low jack safety and some service contracts. I came to know at that time t...

BDR Mutinee And Crisis in Bangladesh

Just when I was thinking we (Bangladeshis) are moving forward with the newly elected Govt bringing hopes for a better democracy, I was shocked with this week's BDR Mutinee and massacre killings of Bangladesh Army's high officials in Pilkhana right in the enter of the capital city Dhaka. This is so sad that it can't be described in written words. We 6 families in Fremont were watching a movie Delhi 6 and at the end of the movie, Shahriar one of my friends told me that he just heard during the intermission of the movie that there is a mutinee happended in Dhaka little earlier against the Army officers. And it's widely suspected that DG of BDR Major General Shakil are of the many high officials who got killed as part of it. We rushed to home to call back to Bangladesh to make sure our family and relatives there are safe. Yes, by almighty, they were. But this is not the same case for many of the others. It's just hopelesss, sad, shocking and distrous in lack of words s...