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...

Sachin Wins 1st Test Against Chennai Chasing 387 with unbeaten century

I followed most of the first test and almost all of the fifth day's magical chase of Tendulkar winning the test against England in Chennai with a fine 103 not out. It was a dream come true for me like many others to see Sachin in this perfect role . Read the details here - http://content-usa.cricinfo.com/indveng/content/current/story/382035.html There are so many good batting points he wrote besides his yesterday and one of those are his 3rd fourth innings century where he is ahead of Lara, equal to Bradman but behind Pointing yet. Last year, Tendulkar made a fourth innings half-century (56*) to defeat Pakistan as I observed that with intense as well like this time as I knew Tendulkar has a poor fourth innings record compared to Ponting or Gavaskar (and almost same as Lara). Not to the same scale worthy of mentioning that amongst all his fourth innings Tendulkar had 36* not out chasing 184 against Zimbabwe in 2001 and 32 * not out chasing 123 against South Africa in 2004. H...