001: /*******************************************************************************
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *******************************************************************************/package org.ofbiz.base.util;
019:
020: import javolution.util.FastList;
021: import javolution.util.FastMap;
022: import javolution.util.FastSet;
023:
024: import java.io.UnsupportedEncodingException;
025: import java.net.URLDecoder;
026: import java.net.URLEncoder;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Set;
031: import java.util.StringTokenizer;
032: import java.util.regex.Matcher;
033: import java.util.regex.Pattern;
034:
035: /**
036: * Misc String Utility Functions
037: *
038: */
039: public class StringUtil {
040:
041: public static final String module = StringUtil.class.getName();
042:
043: /**
044: * Replaces all occurances of oldString in mainString with newString
045: * @param mainString The original string
046: * @param oldString The string to replace
047: * @param newString The string to insert in place of the old
048: * @return mainString with all occurances of oldString replaced by newString
049: */
050: public static String replaceString(String mainString,
051: String oldString, String newString) {
052: if (mainString == null) {
053: return null;
054: }
055: if (oldString == null || oldString.length() == 0) {
056: return mainString;
057: }
058: if (newString == null) {
059: newString = "";
060: }
061:
062: int i = mainString.lastIndexOf(oldString);
063:
064: if (i < 0)
065: return mainString;
066:
067: StringBuffer mainSb = new StringBuffer(mainString);
068:
069: while (i >= 0) {
070: mainSb.replace(i, i + oldString.length(), newString);
071: i = mainString.lastIndexOf(oldString, i - 1);
072: }
073: return mainSb.toString();
074: }
075:
076: /**
077: * Creates a single string from a List of strings seperated by a delimiter.
078: * @param list a list of strings to join
079: * @param delim the delimiter character(s) to use. (null value will join with no delimiter)
080: * @return a String of all values in the list seperated by the delimiter
081: */
082: public static String join(List list, String delim) {
083: if (list == null || list.size() < 1)
084: return null;
085: StringBuffer buf = new StringBuffer();
086: Iterator i = list.iterator();
087:
088: while (i.hasNext()) {
089: buf.append((String) i.next());
090: if (i.hasNext())
091: buf.append(delim);
092: }
093: return buf.toString();
094: }
095:
096: /**
097: * Splits a String on a delimiter into a List of Strings.
098: * @param str the String to split
099: * @param delim the delimiter character(s) to join on (null will split on whitespace)
100: * @return a list of Strings
101: */
102: public static List split(String str, String delim) {
103: List splitList = null;
104: StringTokenizer st = null;
105:
106: if (str == null)
107: return splitList;
108:
109: if (delim != null)
110: st = new StringTokenizer(str, delim);
111: else
112: st = new StringTokenizer(str);
113:
114: if (st != null && st.hasMoreTokens()) {
115: splitList = FastList.newInstance();
116:
117: while (st.hasMoreTokens())
118: splitList.add(st.nextToken());
119: }
120: return splitList;
121: }
122:
123: /**
124: * Encloses each of a List of Strings in quotes.
125: * @param list List of String(s) to quote.
126: */
127: public static List quoteStrList(List list) {
128: List tmpList = list;
129:
130: list = FastList.newInstance();
131: Iterator i = tmpList.iterator();
132:
133: while (i.hasNext()) {
134: String str = (String) i.next();
135:
136: str = "'" + str + "''";
137: list.add(str);
138: }
139: return list;
140: }
141:
142: /**
143: * Creates a Map from an encoded name/value pair string
144: * @param str The string to decode and format
145: * @param trim Trim whitespace off fields
146: * @return a Map of name/value pairs
147: */
148: public static Map strToMap(String str, boolean trim) {
149: if (str == null)
150: return null;
151: Map decodedMap = FastMap.newInstance();
152: List elements = split(str, "|");
153: Iterator i = elements.iterator();
154:
155: while (i.hasNext()) {
156: String s = (String) i.next();
157: List e = split(s, "=");
158:
159: if (e.size() != 2) {
160: continue;
161: }
162: String name = (String) e.get(0);
163: String value = (String) e.get(1);
164: if (trim) {
165: if (name != null) {
166: name = name.trim();
167: }
168: if (value != null) {
169: value = value.trim();
170: }
171: }
172:
173: try {
174: decodedMap.put(URLDecoder.decode(name, "UTF-8"),
175: URLDecoder.decode(value, "UTF-8"));
176: } catch (UnsupportedEncodingException e1) {
177: Debug.logError(e1, module);
178: }
179: }
180: return decodedMap;
181: }
182:
183: /**
184: * Creates a Map from an encoded name/value pair string
185: * @param str The string to decode and format
186: * @return a Map of name/value pairs
187: */
188: public static Map strToMap(String str) {
189: return strToMap(str, false);
190: }
191:
192: /**
193: * Creates an encoded String from a Map of name/value pairs (MUST BE STRINGS!)
194: * @param map The Map of name/value pairs
195: * @return String The encoded String
196: */
197: public static String mapToStr(Map map) {
198: if (map == null)
199: return null;
200: StringBuffer buf = new StringBuffer();
201: Set keySet = map.keySet();
202: Iterator i = keySet.iterator();
203: boolean first = true;
204:
205: while (i.hasNext()) {
206: Object key = i.next();
207: Object value = map.get(key);
208:
209: if (!(key instanceof String) || !(value instanceof String))
210: continue;
211: String encodedName = null;
212: try {
213: encodedName = URLEncoder.encode((String) key, "UTF-8");
214: } catch (UnsupportedEncodingException e) {
215: Debug.logError(e, module);
216: }
217: String encodedValue = null;
218: try {
219: encodedValue = URLEncoder.encode((String) value,
220: "UTF-8");
221: } catch (UnsupportedEncodingException e) {
222: Debug.logError(e, module);
223: }
224:
225: if (first)
226: first = false;
227: else
228: buf.append("|");
229:
230: buf.append(encodedName);
231: buf.append("=");
232: buf.append(encodedValue);
233: }
234: return buf.toString();
235: }
236:
237: /**
238: * Reads a String version of a Map (should contain only strings) and creates a new Map
239: *
240: * @param s String value of a Map ({n1=v1, n2=v2})
241: * @return new Map
242: */
243: public static Map toMap(String s) {
244: Map newMap = FastMap.newInstance();
245: if (s.startsWith("{") && s.endsWith("}")) {
246: s = s.substring(1, s.length() - 1);
247: String[] entry = s.split("\\,\\s");
248: for (int i = 0; i < entry.length; i++) {
249: String[] nv = entry[i].split("\\=");
250: newMap.put(nv[0], nv[1]);
251: }
252: } else {
253: throw new IllegalArgumentException(
254: "String is not from Map.toString()");
255: }
256:
257: return newMap;
258: }
259:
260: /**
261: * Reads a String version of a List (should contain only strings) and creates a new List
262: *
263: * @param s String value of a Map ({n1=v1, n2=v2})
264: * @return new List
265: */
266: public static List toList(String s) {
267: List newList = FastList.newInstance();
268: if (s.startsWith("[") && s.endsWith("]")) {
269: s = s.substring(1, s.length() - 1);
270: String[] entry = s.split("\\,\\s");
271: for (int i = 0; i < entry.length; i++) {
272: newList.add(entry[i]);
273: }
274: } else {
275: throw new IllegalArgumentException(
276: "String is not from List.toString()");
277: }
278:
279: return newList;
280: }
281:
282: /**
283: * Reads a String version of a Set (should contain only strings) and creates a new Set
284: *
285: * @param s String value of a Map ({n1=v1, n2=v2})
286: * @return new List
287: */
288: public static Set toSet(String s) {
289: Set newSet = FastSet.newInstance();
290: if (s.startsWith("[") && s.endsWith("]")) {
291: s = s.substring(1, s.length() - 1);
292: String[] entry = s.split("\\,\\s");
293: for (int i = 0; i < entry.length; i++) {
294: newSet.add(entry[i]);
295: }
296: } else {
297: throw new IllegalArgumentException(
298: "String is not from Set.toString()");
299: }
300:
301: return newSet;
302: }
303:
304: /**
305: * Create a Map from a List of keys and a List of values
306: * @param keys List of keys
307: * @param values List of values
308: * @return Map of combined lists
309: * @throws IllegalArgumentException When either List is null or the sizes do not equal
310: */
311: public static Map createMap(List keys, List values) {
312: if (keys == null || values == null
313: || keys.size() != values.size()) {
314: throw new IllegalArgumentException(
315: "Keys and Values cannot be null and must be the same size");
316: }
317: Map newMap = FastMap.newInstance();
318: for (int i = 0; i < keys.size(); i++) {
319: newMap.put(keys.get(i), values.get(i));
320: }
321: return newMap;
322: }
323:
324: /** Make sure the string starts with a forward slash but does not end with one; converts back-slashes to forward-slashes; if in String is null or empty, returns zero length string. */
325: public static String cleanUpPathPrefix(String prefix) {
326: if (prefix == null || prefix.length() == 0)
327: return "";
328:
329: StringBuffer cppBuff = new StringBuffer(prefix.replace('\\',
330: '/'));
331:
332: if (cppBuff.charAt(0) != '/') {
333: cppBuff.insert(0, '/');
334: }
335: if (cppBuff.charAt(cppBuff.length() - 1) == '/') {
336: cppBuff.deleteCharAt(cppBuff.length() - 1);
337: }
338: return cppBuff.toString();
339: }
340:
341: /** Removes all spaces from a string */
342: public static String removeSpaces(String str) {
343: return removeRegex(str, "[\\ ]");
344: }
345:
346: public static String toHexString(byte[] bytes) {
347: StringBuffer buf = new StringBuffer(bytes.length * 2);
348: for (int i = 0; i < bytes.length; i++) {
349: buf.append(hexChar[(bytes[i] & 0xf0) >>> 4]);
350: buf.append(hexChar[bytes[i] & 0x0f]);
351: }
352: return buf.toString();
353:
354: }
355:
356: public static String cleanHexString(String str) {
357: StringBuffer buf = new StringBuffer();
358: for (int i = 0; i < str.length(); i++) {
359: if (str.charAt(i) != 32 && str.charAt(i) != ':') {
360: buf.append(str.charAt(i));
361: }
362: }
363: return buf.toString();
364: }
365:
366: public static byte[] fromHexString(String str) {
367: str = cleanHexString(str);
368: int stringLength = str.length();
369: if ((stringLength & 0x1) != 0) {
370: throw new IllegalArgumentException(
371: "fromHexString requires an even number of hex characters");
372: }
373: byte[] b = new byte[stringLength / 2];
374:
375: for (int i = 0, j = 0; i < stringLength; i += 2, j++) {
376: int high = convertChar(str.charAt(i));
377: int low = convertChar(str.charAt(i + 1));
378: b[j] = (byte) ((high << 4) | low);
379: }
380: return b;
381: }
382:
383: private static char[] hexChar = { '0', '1', '2', '3', '4', '5',
384: '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
385:
386: public static int convertChar(char c) {
387: if ('0' <= c && c <= '9') {
388: return c - '0';
389: } else if ('a' <= c && c <= 'f') {
390: return c - 'a' + 0xa;
391: } else if ('A' <= c && c <= 'F') {
392: return c - 'A' + 0xa;
393: } else {
394: throw new IllegalArgumentException(
395: "Invalid hex character: [" + c + "]");
396: }
397: }
398:
399: public static char[] encodeInt(int i, int j, char digestChars[]) {
400: if (i < 16) {
401: digestChars[j] = '0';
402: }
403: j++;
404: do {
405: digestChars[j--] = hexChar[i & 0xf];
406: i >>>= 4;
407: } while (i != 0);
408: return digestChars;
409: }
410:
411: /** Removes all non-numbers from str */
412: public static String removeNonNumeric(String str) {
413: return removeRegex(str, "[\\D]");
414: }
415:
416: /** Removes all numbers from str */
417: public static String removeNumeric(String str) {
418: return removeRegex(str, "[\\d]");
419: }
420:
421: /**
422: * @param str
423: * @param regex
424: * Removes all matches of regex from a str
425: */
426: private static String removeRegex(String str, String regex) {
427: Pattern pattern = Pattern.compile(regex);
428: Matcher matcher = pattern.matcher(str);
429: return matcher.replaceAll("");
430: }
431:
432: /**
433: * Add the number to the string, keeping (padding to min of original length)
434: *
435: * @return
436: */
437: public static String addToNumberString(String numberString,
438: long addAmount) {
439: if (numberString == null)
440: return null;
441:
442: int origLength = numberString.length();
443: long number = Long.parseLong(numberString);
444: return padNumberString(Long.toString(number + addAmount),
445: origLength);
446: }
447:
448: public static String padNumberString(String numberString,
449: int targetMinLength) {
450: StringBuffer outStrBfr = new StringBuffer(numberString);
451: while (targetMinLength > outStrBfr.length()) {
452: outStrBfr.insert(0, '0');
453: }
454: return outStrBfr.toString();
455: }
456:
457: /**
458: * Translates various HTML characters in a string so that the string can be displayed in a browser safely
459: * <p>
460: * This function is useful in preventing user-supplied text from containing HTML markup, such as in a message board or
461: * guest book application. The optional arguments doubleQuotes and singleQuotes allow the control of the substitution of
462: * the quote characters. The default is to translate them with the HTML equivalent.
463: * </p>
464: * The translations performed are: <ol>
465: * <li>'&' (ampersand) becomes '&'
466: * <li>'"' (double quote) becomes '"' when doubleQuotes is true.
467: * <li>''' (single quote) becomes ''' when singleQuotes is true.
468: * <li>'<' (less than) becomes '<'
469: * <li>'>' (greater than) becomes '>'
470: * <li>\n (Carriage Return) becomes '<br>gt;'
471: * </ol>
472: */
473: public static String htmlSpecialChars(String html,
474: boolean doubleQuotes, boolean singleQuotes, boolean insertBR) {
475: html = StringUtil.replaceString(html, "&", "&");
476: html = StringUtil.replaceString(html, "<", "<");
477: html = StringUtil.replaceString(html, ">", ">");
478: if (doubleQuotes) {
479: html = StringUtil.replaceString(html, "\"", """);
480: }
481: if (singleQuotes) {
482: html = StringUtil.replaceString(html, "'", "'");
483: }
484: if (insertBR) {
485: html = StringUtil.replaceString(html, "\n", "<br>");
486: }
487:
488: return html;
489: }
490:
491: public static String htmlSpecialChars(String html) {
492: return htmlSpecialChars(html, true, true, true);
493: }
494:
495: }
|