View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.util;
5   
6   import java.util.ArrayList;
7   import java.util.Iterator;
8   import java.util.List;
9   
10  public class StringUtil {
11  
12  	public static final String[] EMPTY_STRINGS = new String[0];
13      private static final boolean supportsUTF8 = System.getProperty("net.sourceforge.pmd.supportUTF8", "no").equals("yes");
14  
15      public static String replaceString(String original, char oldChar, String newString) {
16  
17      	String fixedNew = newString == null ? "" : newString;
18  
19          StringBuffer desc = new StringBuffer();
20          int index = original.indexOf(oldChar);
21          int last = 0;
22          while (index != -1) {
23              desc.append(original.substring(last, index));
24              desc.append(fixedNew);
25              last = index + 1;
26              index = original.indexOf(oldChar, last);
27          }
28          desc.append(original.substring(last));
29          return desc.toString();
30      }
31  
32      public static String replaceString(String original, String oldString, String newString) {
33  
34      	String fixedNew = newString == null ? "" : newString;
35  
36          StringBuffer desc = new StringBuffer();
37          int index = original.indexOf(oldString);
38          int last = 0;
39          while (index != -1) {
40              desc.append(original.substring(last, index));
41              desc.append(fixedNew);
42              last = index + oldString.length();
43              index = original.indexOf(oldString, last);
44          }
45          desc.append(original.substring(last));
46          return desc.toString();
47      }
48  
49      /**
50       * Appends to a StringBuffer the String src where non-ASCII and
51       * XML special chars are escaped.
52       *
53       * @param buf The destination XML stream
54       * @param src The String to append to the stream
55       */
56      public static void appendXmlEscaped(StringBuffer buf, String src) {
57          appendXmlEscaped(buf, src, supportsUTF8);
58      }
59  
60      public static String htmlEncode(String string) {
61          String encoded = StringUtil.replaceString(string, '&', "&");
62          encoded = StringUtil.replaceString(encoded, '<', "&lt;");
63          return StringUtil.replaceString(encoded, '>', "&gt;");
64      }
65  
66      // TODO - unify the method above with the one below
67  
68      private static void appendXmlEscaped(StringBuffer buf, String src, boolean supportUTF8) {
69          char c;
70          for (int i = 0; i < src.length(); i++) {
71              c = src.charAt(i);
72              if (c > '~') {// 126
73                  if (!supportUTF8) {
74                      buf.append("&#x").append(Integer.toHexString(c)).append(';');
75                  } else {
76                      buf.append(c);
77                  }
78              } else if (c == '&')
79                  buf.append("&amp;");
80              else if (c == '"')
81                  buf.append("&quot;");
82              else if (c == '<')
83                  buf.append("&lt;");
84              else if (c == '>')
85                  buf.append("&gt;");
86              else
87                  buf.append(c);
88          }
89      }
90  
91  	/**
92  	 * Parses the input source using the delimiter specified. This method is much
93  	 * faster than using the StringTokenizer or String.split(char) approach and
94  	 * serves as a replacement for String.split() for JDK1.3 that doesn't have it.
95       *
96       * FIXME - we're on JDK 1.4 now, can we replace this with String.split?
97  	 *
98  	 * @param source String
99  	 * @param delimiter char
100 	 * @return String[]
101 	 */
102 	public static String[] substringsOf(String source, char delimiter) {
103 
104 		if (source == null || source.length() == 0) {
105             return EMPTY_STRINGS;
106         }
107 
108 		int delimiterCount = 0;
109 		int length = source.length();
110 		char[] chars = source.toCharArray();
111 
112 		for (int i=0; i<length; i++) {
113 			if (chars[i] == delimiter) delimiterCount++;
114 			}
115 
116 		if (delimiterCount == 0) return new String[] { source };
117 
118 		String results[] = new String[delimiterCount+1];
119 
120 		int i = 0;
121 		int offset = 0;
122 
123 		while (offset <= length) {
124 			int pos = source.indexOf(delimiter, offset);
125 			if (pos < 0) pos = length;
126 			results[i++] = pos == offset ? "" : source.substring(offset, pos);
127 			offset = pos + 1;
128 			}
129 
130 		return results;
131 	}
132 
133 	/**
134 	 * Much more efficient than StringTokenizer.
135 	 *
136 	 * @param str String
137 	 * @param separator char
138 	 * @return String[]
139 	 */
140 	  public static String[] substringsOf(String str, String separator) {
141 
142 	        if (str == null || str.length() == 0) {
143 	            return EMPTY_STRINGS;
144 	        }
145 
146 	        int index = str.indexOf(separator);
147 	        if (index == -1) {
148 	            return new String[]{str};
149 	        }
150 
151 	        List<String> list = new ArrayList<String>();
152 	        int currPos = 0;
153 	        int len = separator.length();
154 	        while (index != -1) {
155 	            list.add(str.substring(currPos, index));
156 	            currPos = index + len;
157 	            index = str.indexOf(separator, currPos);
158 	        }
159 	        list.add(str.substring(currPos));
160 	        return list.toArray(new String[list.size()]);
161 	    }
162 
163 
164 	/**
165 	 * Copies the elements returned by the iterator onto the string buffer
166 	 * each delimited by the separator.
167 	 *
168 	 * @param sb StringBuffer
169 	 * @param iter Iterator
170 	 * @param separator String
171 	 */
172 	public static void asStringOn(StringBuffer sb, Iterator iter, String separator) {
173 
174 	    if (!iter.hasNext()) return;
175 
176 	    sb.append(iter.next());
177 
178 	    while (iter.hasNext()) {
179 	    	sb.append(separator);
180 	        sb.append(iter.next());
181 	    }
182 	}
183 	/**
184 	 * Return the length of the shortest string in the array.
185 	 * If any one of them is null then it returns 0.
186 	 *
187 	 * @param strings String[]
188 	 * @return int
189 	 */
190 	public static int lengthOfShortestIn(String[] strings) {
191 
192 		int minLength = Integer.MAX_VALUE;
193 
194 		for (int i=0; i<strings.length; i++) {
195 			if (strings[i] == null) return 0;
196 			minLength = Math.min(minLength, strings[i].length());
197 		}
198 
199 		return minLength;
200 	}
201 
202 	/**
203 	 * Determine the maximum number of common leading whitespace characters
204 	 * the strings share in the same sequence. Useful for determining how
205 	 * many leading characters can be removed to shift all the text in the
206 	 * strings to the left without misaligning them.
207 	 *
208 	 * @param strings String[]
209 	 * @return int
210 	 */
211 	public static int maxCommonLeadingWhitespaceForAll(String[] strings) {
212 
213 		int shortest = lengthOfShortestIn(strings);
214 		if (shortest == 0) return 0;
215 
216 		char[] matches = new char[shortest];
217 
218 		String str;
219 		for (int m=0; m<matches.length; m++) {
220 			matches[m] = strings[0].charAt(m);
221 			if (!Character.isWhitespace(matches[m])) return m;
222 			for (int i=0; i<strings.length; i++) {
223 				str = strings[i];
224 				if (str.charAt(m) != matches[m])  return m;
225 				}
226 		}
227 
228 		return shortest;
229 	}
230 
231 	/**
232 	 * Trims off the leading characters off the strings up to the trimDepth
233 	 * specified. Returns the same strings if trimDepth = 0
234 	 *
235 	 * @param strings
236 	 * @param trimDepth
237 	 * @return String[]
238 	 */
239 	public static String[] trimStartOn(String[] strings, int trimDepth) {
240 
241 		if (trimDepth == 0) return strings;
242 
243 		String[] results = new String[strings.length];
244 		for (int i=0; i<strings.length; i++) {
245 			results[i] = strings[i].substring(trimDepth);
246 		}
247 		return results;
248    }
249 
250     /**
251      * Left pads a string.
252      * @param s The String to pad
253      * @param length The desired minimum length of the resulting padded String
254      * @return The resulting left padded String
255      */
256     public static String lpad(String s, int length) {
257          String res = s;
258          if (length - s.length() > 0) {
259              char [] arr = new char[length - s.length()];
260              java.util.Arrays.fill(arr, ' ');
261              res = new StringBuffer(length).append(arr).append(s).toString();
262          }
263          return res;
264     }
265 
266     /**
267      * Are the two String values the same.
268      * The Strings can be optionally trimmed before checking.
269      * The Strings can be optionally compared ignoring case.
270      * The Strings can be have embedded whitespace standardized before comparing.
271      * Two null values are treated as equal.
272      *
273      * @param s1 The first String.
274      * @param s2 The second String.
275      * @param trim Indicates if the Strings should be trimmed before comparison.
276      * @param ignoreCase Indicates if the case of the Strings should ignored during comparison.
277      * @param standardizeWhitespace Indicates if the embedded whitespace should be standardized before comparison.
278      * @return <code>true</code> if the Strings are the same, <code>false</code> otherwise.
279      */
280     public static boolean isSame(String s1, String s2, boolean trim, boolean ignoreCase, boolean standardizeWhitespace) {
281 		if (s1 == s2) {
282 			return true;
283 		} else if (s1 == null || s2 == null) {
284 			return false;
285 		} else {
286 			if (trim) {
287 				s1 = s1.trim();
288 				s2 = s2.trim();
289 			}
290 			if (standardizeWhitespace) {
291 				// Replace all whitespace with a standard single space character.
292 				s1 = s1.replaceAll("\\s+", " ");
293 				s2 = s2.replaceAll("\\s+", " ");
294 			}
295 			return ignoreCase ? s1.equalsIgnoreCase(s2) : s1.equals(s2);
296 		}
297     }
298 }