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 private static final String[] ENTITIES;
15
16 static {
17 ENTITIES = new String[256 - 126];
18 for (int i = 126; i <= 255; i++) {
19 ENTITIES[i - 126] = "&#" + i + ';';
20 }
21 }
22
23 public static String replaceString(String original, char oldChar, String newString) {
24
25 String fixedNew = newString == null ? "" : newString;
26
27 StringBuffer desc = new StringBuffer();
28 int index = original.indexOf(oldChar);
29 int last = 0;
30 while (index != -1) {
31 desc.append(original.substring(last, index));
32 desc.append(fixedNew);
33 last = index + 1;
34 index = original.indexOf(oldChar, last);
35 }
36 desc.append(original.substring(last));
37 return desc.toString();
38 }
39
40 public static String replaceString(String original, String oldString, String newString) {
41
42 String fixedNew = newString == null ? "" : newString;
43
44 StringBuffer desc = new StringBuffer();
45 int index = original.indexOf(oldString);
46 int last = 0;
47 while (index != -1) {
48 desc.append(original.substring(last, index));
49 desc.append(fixedNew);
50 last = index + oldString.length();
51 index = original.indexOf(oldString, last);
52 }
53 desc.append(original.substring(last));
54 return desc.toString();
55 }
56
57 /***
58 * Appends to a StringBuffer the String src where non-ASCII and
59 * XML special chars are escaped.
60 *
61 * @param buf The destination XML stream
62 * @param src The String to append to the stream
63 */
64 public static void appendXmlEscaped(StringBuffer buf, String src) {
65 appendXmlEscaped(buf, src, supportsUTF8);
66 }
67
68 public static String htmlEncode(String string) {
69 String encoded = StringUtil.replaceString(string, '&', "&");
70 encoded = StringUtil.replaceString(encoded, '<', "<");
71 return StringUtil.replaceString(encoded, '>', ">");
72 }
73
74
75
76 private static void appendXmlEscaped(StringBuffer buf, String src, boolean supportUTF8) {
77 char c;
78 for (int i = 0; i < src.length(); i++) {
79 c = src.charAt(i);
80 if (c > '~') {
81 if (!supportUTF8) {
82 if (c <= 255) {
83 buf.append(ENTITIES[c - 126]);
84 } else {
85 buf.append("&u").append(Integer.toHexString(c)).append(';');
86 }
87 } else {
88 buf.append(c);
89 }
90 } else if (c == '&')
91 buf.append("&");
92 else if (c == '"')
93 buf.append(""");
94 else if (c == '<')
95 buf.append("<");
96 else if (c == '>')
97 buf.append(">");
98 else
99 buf.append(c);
100 }
101 }
102
103 /***
104 * Parses the input source using the delimiter specified. This method is much
105 * faster than using the StringTokenizer or String.split(char) approach and
106 * serves as a replacement for String.split() for JDK1.3 that doesn't have it.
107 *
108 * @param source String
109 * @param delimiter char
110 * @return String[]
111 */
112 public static String[] substringsOf(String source, char delimiter) {
113
114 if (source == null || source.length() == 0) {
115 return EMPTY_STRINGS;
116 }
117
118 int delimiterCount = 0;
119 int length = source.length();
120 char[] chars = source.toCharArray();
121
122 for (int i=0; i<length; i++) {
123 if (chars[i] == delimiter) delimiterCount++;
124 }
125
126 if (delimiterCount == 0) return new String[] { source };
127
128 String results[] = new String[delimiterCount+1];
129
130 int i = 0;
131 int offset = 0;
132
133 while (offset <= length) {
134 int pos = source.indexOf(delimiter, offset);
135 if (pos < 0) pos = length;
136 results[i++] = pos == offset ? "" : source.substring(offset, pos);
137 offset = pos + 1;
138 }
139
140 return results;
141 }
142
143 /***
144 * Much more efficient than StringTokenizer.
145 *
146 * @param str String
147 * @param separator char
148 * @return String[]
149 */
150 public static String[] substringsOf(String str, String separator) {
151
152 if (str == null || str.length() == 0) {
153 return EMPTY_STRINGS;
154 }
155
156 int index = str.indexOf(separator);
157 if (index == -1) {
158 return new String[]{str};
159 }
160
161 List list = new ArrayList();
162 int currPos = 0;
163 int len = separator.length();
164 while (index != -1) {
165 list.add(str.substring(currPos, index));
166 currPos = index + len;
167 index = str.indexOf(separator, currPos);
168 }
169 list.add(str.substring(currPos));
170 return (String[]) list.toArray(new String[list.size()]);
171 }
172
173
174 /***
175 * Copies the elements returned by the iterator onto the string buffer
176 * each delimited by the separator.
177 *
178 * @param sb StringBuffer
179 * @param iter Iterator
180 * @param separator String
181 */
182 public static void asStringOn(StringBuffer sb, Iterator iter, String separator) {
183
184 if (!iter.hasNext()) return;
185
186 sb.append(iter.next());
187
188 while (iter.hasNext()) {
189 sb.append(separator);
190 sb.append(iter.next());
191 }
192 }
193 /***
194 * Return the length of the shortest string in the array.
195 * If any one of them is null then it returns 0.
196 *
197 * @param strings String[]
198 * @return int
199 */
200 public static int lengthOfShortestIn(String[] strings) {
201
202 int minLength = Integer.MAX_VALUE;
203
204 for (int i=0; i<strings.length; i++) {
205 if (strings[i] == null) return 0;
206 minLength = Math.min(minLength, strings[i].length());
207 }
208
209 return minLength;
210 }
211
212 /***
213 * Determine the maximum number of common leading whitespace characters
214 * the strings share in the same sequence. Useful for determining how
215 * many leading characters can be removed to shift all the text in the
216 * strings to the left without misaligning them.
217 *
218 * @param strings String[]
219 * @return int
220 */
221 public static int maxCommonLeadingWhitespaceForAll(String[] strings) {
222
223 int shortest = lengthOfShortestIn(strings);
224 if (shortest == 0) return 0;
225
226 char[] matches = new char[shortest];
227
228 String str;
229 for (int m=0; m<matches.length; m++) {
230 matches[m] = strings[0].charAt(m);
231 if (!Character.isWhitespace(matches[m])) return m;
232 for (int i=0; i<strings.length; i++) {
233 str = strings[i];
234 if (str.charAt(m) != matches[m]) return m;
235 }
236 }
237
238 return shortest;
239 }
240
241 /***
242 * Trims off the leading characters off the strings up to the trimDepth
243 * specified. Returns the same strings if trimDepth = 0
244 *
245 * @param strings
246 * @param trimDepth
247 * @return String[]
248 */
249 public static String[] trimStartOn(String[] strings, int trimDepth) {
250
251 if (trimDepth == 0) return strings;
252
253 String[] results = new String[strings.length];
254 for (int i=0; i<strings.length; i++) {
255 results[i] = strings[i].substring(trimDepth);
256 }
257 return results;
258 }
259 }