001: package org.apache.roller.util;
002:
003: import java.io.BufferedInputStream;
004: import java.io.BufferedOutputStream;
005: import java.io.File;
006: import java.io.FileInputStream;
007: import java.io.FileOutputStream;
008: import java.io.IOException;
009: import java.io.InputStream;
010: import java.io.OutputStream;
011: import java.io.UnsupportedEncodingException;
012: import java.net.URLDecoder;
013: import java.net.URLEncoder;
014: import java.security.MessageDigest;
015: import java.util.Arrays;
016: import java.util.Collections;
017: import java.util.List;
018: import java.util.Locale;
019: import java.util.NoSuchElementException;
020: import java.util.StringTokenizer;
021:
022: import org.apache.commons.lang.StringEscapeUtils;
023: import org.apache.commons.lang.StringUtils;
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026:
027: import sun.misc.BASE64Decoder;
028: import sun.misc.BASE64Encoder;
029:
030: /**
031: * General purpose utilities, not for use in templates.
032: */
033: public class Utilities {
034: /** The <code>Log</code> instance for this class. */
035: private static Log mLogger = LogFactory.getLog(Utilities.class);
036:
037: public final static String TAG_SPLIT_CHARS = " ,\n\r\f\t";
038:
039: //------------------------------------------------------------------------
040: /** Strip jsessionid off of a URL */
041: public static String stripJsessionId(String url) {
042: // Strip off jsessionid found in referer URL
043: int startPos = url.indexOf(";jsessionid=");
044: if (startPos != -1) {
045: int endPos = url.indexOf("?", startPos);
046: if (endPos == -1) {
047: url = url.substring(0, startPos);
048: } else {
049: url = url.substring(0, startPos)
050: + url.substring(endPos, url.length());
051: }
052: }
053: return url;
054: }
055:
056: //------------------------------------------------------------------------
057: /**
058: * Escape, but do not replace HTML.
059: * The default behaviour is to escape ampersands.
060: */
061: public static String escapeHTML(String s) {
062: return escapeHTML(s, true);
063: }
064:
065: //------------------------------------------------------------------------
066: /**
067: * Escape, but do not replace HTML.
068: * @param escapeAmpersand Optionally escape
069: * ampersands (&).
070: */
071: public static String escapeHTML(String s, boolean escapeAmpersand) {
072: // got to do amp's first so we don't double escape
073: if (escapeAmpersand) {
074: s = StringUtils.replace(s, "&", "&");
075: }
076: s = StringUtils.replace(s, " ", " ");
077: s = StringUtils.replace(s, "\"", """);
078: s = StringUtils.replace(s, "<", "<");
079: s = StringUtils.replace(s, ">", ">");
080: return s;
081: }
082:
083: public static String unescapeHTML(String str) {
084: return StringEscapeUtils.unescapeHtml(str);
085: }
086:
087: //------------------------------------------------------------------------
088: /**
089: * Remove occurences of html, defined as any text
090: * between the characters "<" and ">". Replace
091: * any HTML tags with a space.
092: */
093: public static String removeHTML(String str) {
094: return removeHTML(str, true);
095: }
096:
097: /**
098: * Remove occurences of html, defined as any text
099: * between the characters "<" and ">".
100: * Optionally replace HTML tags with a space.
101: *
102: * @param str
103: * @param addSpace
104: * @return
105: */
106: public static String removeHTML(String str, boolean addSpace) {
107: if (str == null)
108: return "";
109: StringBuffer ret = new StringBuffer(str.length());
110: int start = 0;
111: int beginTag = str.indexOf("<");
112: int endTag = 0;
113: if (beginTag == -1)
114: return str;
115:
116: while (beginTag >= start) {
117: if (beginTag > 0) {
118: ret.append(str.substring(start, beginTag));
119:
120: // replace each tag with a space (looks better)
121: if (addSpace)
122: ret.append(" ");
123: }
124: endTag = str.indexOf(">", beginTag);
125:
126: // if endTag found move "cursor" forward
127: if (endTag > -1) {
128: start = endTag + 1;
129: beginTag = str.indexOf("<", start);
130: }
131: // if no endTag found, get rest of str and break
132: else {
133: ret.append(str.substring(beginTag));
134: break;
135: }
136: }
137: // append everything after the last endTag
138: if (endTag > -1 && endTag + 1 < str.length()) {
139: ret.append(str.substring(endTag + 1));
140: }
141: return ret.toString().trim();
142: }
143:
144: //------------------------------------------------------------------------
145: /** Run both removeHTML and escapeHTML on a string.
146: * @param s String to be run through removeHTML and escapeHTML.
147: * @return String with HTML removed and HTML special characters escaped.
148: */
149: public static String removeAndEscapeHTML(String s) {
150: if (s == null)
151: return "";
152: else
153: return Utilities.escapeHTML(Utilities.removeHTML(s));
154: }
155:
156: //------------------------------------------------------------------------
157: /**
158: * Autoformat.
159: */
160: public static String autoformat(String s) {
161: String ret = StringUtils.replace(s, "\n", "<br />");
162: return ret;
163: }
164:
165: //------------------------------------------------------------------------
166: /**
167: * Replaces occurences of non-alphanumeric characters with an underscore.
168: */
169: public static String replaceNonAlphanumeric(String str) {
170: return replaceNonAlphanumeric(str, '_');
171: }
172:
173: //------------------------------------------------------------------------
174: /**
175: * Replaces occurences of non-alphanumeric characters with a
176: * supplied char.
177: */
178: public static String replaceNonAlphanumeric(String str, char subst) {
179: StringBuffer ret = new StringBuffer(str.length());
180: char[] testChars = str.toCharArray();
181: for (int i = 0; i < testChars.length; i++) {
182: if (Character.isLetterOrDigit(testChars[i])) {
183: ret.append(testChars[i]);
184: } else {
185: ret.append(subst);
186: }
187: }
188: return ret.toString();
189: }
190:
191: //------------------------------------------------------------------------
192: /**
193: * Remove occurences of non-alphanumeric characters.
194: */
195: public static String removeNonAlphanumeric(String str) {
196: StringBuffer ret = new StringBuffer(str.length());
197: char[] testChars = str.toCharArray();
198: for (int i = 0; i < testChars.length; i++) {
199: // MR: Allow periods in page links
200: if (Character.isLetterOrDigit(testChars[i])
201: || testChars[i] == '.') {
202: ret.append(testChars[i]);
203: }
204: }
205: return ret.toString();
206: }
207:
208: //------------------------------------------------------------------------
209: /**
210: * @param stringArray
211: * @param delim
212: * @return
213: */
214: public static String stringArrayToString(String[] stringArray,
215: String delim) {
216: String ret = "";
217: for (int i = 0; i < stringArray.length; i++) {
218: if (ret.length() > 0)
219: ret = ret + delim + stringArray[i];
220: else
221: ret = stringArray[i];
222: }
223: return ret;
224: }
225:
226: //--------------------------------------------------------------------------
227: /** Convert string to string array. */
228: public static String[] stringToStringArray(String instr,
229: String delim) throws NoSuchElementException,
230: NumberFormatException {
231: StringTokenizer toker = new StringTokenizer(instr, delim);
232: String stringArray[] = new String[toker.countTokens()];
233: int i = 0;
234:
235: while (toker.hasMoreTokens()) {
236: stringArray[i++] = toker.nextToken();
237: }
238: return stringArray;
239: }
240:
241: //--------------------------------------------------------------------------
242: /** Convert string to integer array. */
243: public static int[] stringToIntArray(String instr, String delim)
244: throws NoSuchElementException, NumberFormatException {
245: StringTokenizer toker = new StringTokenizer(instr, delim);
246: int intArray[] = new int[toker.countTokens()];
247: int i = 0;
248:
249: while (toker.hasMoreTokens()) {
250: String sInt = toker.nextToken();
251: int nInt = Integer.parseInt(sInt);
252: intArray[i++] = new Integer(nInt).intValue();
253: }
254: return intArray;
255: }
256:
257: //-------------------------------------------------------------------
258: /** Convert integer array to a string. */
259: public static String intArrayToString(int[] intArray) {
260: String ret = "";
261: for (int i = 0; i < intArray.length; i++) {
262: if (ret.length() > 0)
263: ret = ret + "," + Integer.toString(intArray[i]);
264: else
265: ret = Integer.toString(intArray[i]);
266: }
267: return ret;
268: }
269:
270: //------------------------------------------------------------------------
271: public static void copyFile(File from, File to) throws IOException {
272: InputStream in = null;
273: OutputStream out = null;
274:
275: try {
276: in = new FileInputStream(from);
277: } catch (IOException ex) {
278: throw new IOException(
279: "Utilities.copyFile: opening input stream '"
280: + from.getPath() + "', " + ex.getMessage());
281: }
282:
283: try {
284: out = new FileOutputStream(to);
285: } catch (Exception ex) {
286: try {
287: in.close();
288: } catch (IOException ex1) {
289: }
290: throw new IOException(
291: "Utilities.copyFile: opening output stream '"
292: + to.getPath() + "', " + ex.getMessage());
293: }
294:
295: copyInputToOutput(in, out, from.length());
296: }
297:
298: //------------------------------------------------------------------------
299: /**
300: * Utility method to copy an input stream to an output stream.
301: * Wraps both streams in buffers. Ensures right numbers of bytes copied.
302: */
303: public static void copyInputToOutput(InputStream input,
304: OutputStream output, long byteCount) throws IOException {
305: int bytes;
306: long length;
307:
308: BufferedInputStream in = new BufferedInputStream(input);
309: BufferedOutputStream out = new BufferedOutputStream(output);
310:
311: byte[] buffer;
312: buffer = new byte[8192];
313:
314: for (length = byteCount; length > 0;) {
315: bytes = (int) (length > 8192 ? 8192 : length);
316:
317: try {
318: bytes = in.read(buffer, 0, bytes);
319: } catch (IOException ex) {
320: try {
321: in.close();
322: out.close();
323: } catch (IOException ex1) {
324: }
325: throw new IOException("Reading input stream, "
326: + ex.getMessage());
327: }
328:
329: if (bytes < 0)
330: break;
331:
332: length -= bytes;
333:
334: try {
335: out.write(buffer, 0, bytes);
336: } catch (IOException ex) {
337: try {
338: in.close();
339: out.close();
340: } catch (IOException ex1) {
341: }
342: throw new IOException("Writing output stream, "
343: + ex.getMessage());
344: }
345: }
346:
347: try {
348: in.close();
349: out.close();
350: } catch (IOException ex) {
351: throw new IOException("Closing file streams, "
352: + ex.getMessage());
353: }
354: }
355:
356: //------------------------------------------------------------------------
357: public static void copyInputToOutput(InputStream input,
358: OutputStream output) throws IOException {
359: BufferedInputStream in = new BufferedInputStream(input);
360: BufferedOutputStream out = new BufferedOutputStream(output);
361: byte buffer[] = new byte[8192];
362: for (int count = 0; count != -1;) {
363: count = in.read(buffer, 0, 8192);
364: if (count != -1)
365: out.write(buffer, 0, count);
366: }
367:
368: try {
369: in.close();
370: out.close();
371: } catch (IOException ex) {
372: throw new IOException("Closing file streams, "
373: + ex.getMessage());
374: }
375: }
376:
377: /**
378: * Encode a string using algorithm specified in web.xml and return the
379: * resulting encrypted password. If exception, the plain credentials
380: * string is returned
381: *
382: * @param password Password or other credentials to use in authenticating
383: * this username
384: * @param algorithm Algorithm used to do the digest
385: *
386: * @return encypted password based on the algorithm.
387: */
388: public static String encodePassword(String password,
389: String algorithm) {
390: byte[] unencodedPassword = password.getBytes();
391:
392: MessageDigest md = null;
393:
394: try {
395: // first create an instance, given the provider
396: md = MessageDigest.getInstance(algorithm);
397: } catch (Exception e) {
398: mLogger.error("Exception: " + e);
399: return password;
400: }
401:
402: md.reset();
403:
404: // call the update method one or more times
405: // (useful when you don't know the size of your data, eg. stream)
406: md.update(unencodedPassword);
407:
408: // now calculate the hash
409: byte[] encodedPassword = md.digest();
410:
411: StringBuffer buf = new StringBuffer();
412:
413: for (int i = 0; i < encodedPassword.length; i++) {
414: if ((encodedPassword[i] & 0xff) < 0x10) {
415: buf.append("0");
416: }
417:
418: buf.append(Long.toString(encodedPassword[i] & 0xff, 16));
419: }
420:
421: return buf.toString();
422: }
423:
424: /**
425: * Encode a string using Base64 encoding. Used when storing passwords
426: * as cookies.
427: *
428: * This is weak encoding in that anyone can use the decodeString
429: * routine to reverse the encoding.
430: *
431: * @param str
432: * @return String
433: * @throws IOException
434: */
435: public static String encodeString(String str) throws IOException {
436: BASE64Encoder encoder = new BASE64Encoder();
437: String encodedStr = encoder.encodeBuffer(str.getBytes());
438:
439: return (encodedStr.trim());
440: }
441:
442: /**
443: * Decode a string using Base64 encoding.
444: *
445: * @param str
446: * @return String
447: * @throws IOException
448: */
449: public static String decodeString(String str) throws IOException {
450: BASE64Decoder dec = new BASE64Decoder();
451: String value = new String(dec.decodeBuffer(str));
452:
453: return (value);
454: }
455:
456: /**
457: * Strips HTML and truncates.
458: */
459: public static String truncate(String str, int lower, int upper,
460: String appendToEnd) {
461: // strip markup from the string
462: String str2 = removeHTML(str, false);
463:
464: // quickly adjust the upper if it is set lower than 'lower'
465: if (upper < lower) {
466: upper = lower;
467: }
468:
469: // now determine if the string fits within the upper limit
470: // if it does, go straight to return, do not pass 'go' and collect $200
471: if (str2.length() > upper) {
472: // the magic location int
473: int loc;
474:
475: // first we determine where the next space appears after lower
476: loc = str2.lastIndexOf(' ', upper);
477:
478: // now we'll see if the location is greater than the lower limit
479: if (loc >= lower) {
480: // yes it was, so we'll cut it off here
481: str2 = str2.substring(0, loc);
482: } else {
483: // no it wasnt, so we'll cut it off at the upper limit
484: str2 = str2.substring(0, upper);
485: loc = upper;
486: }
487:
488: // the string was truncated, so we append the appendToEnd String
489: str2 = str2 + appendToEnd;
490: }
491:
492: return str2;
493: }
494:
495: /**
496: * This method based on code from the String taglib at Apache Jakarta:
497: * http://cvs.apache.org/viewcvs/jakarta-taglibs/string/src/org/apache/taglibs/string/util/StringW.java?rev=1.16&content-type=text/vnd.viewcvs-markup
498: * Copyright (c) 1999 The Apache Software Foundation.
499: * Author: timster@mac.com
500: *
501: * @param str
502: * @param lower
503: * @param upper
504: * @param appendToEnd
505: * @return
506: */
507: public static String truncateNicely(String str, int lower,
508: int upper, String appendToEnd) {
509: // strip markup from the string
510: String str2 = removeHTML(str, false);
511: boolean diff = (str2.length() < str.length());
512:
513: // quickly adjust the upper if it is set lower than 'lower'
514: if (upper < lower) {
515: upper = lower;
516: }
517:
518: // now determine if the string fits within the upper limit
519: // if it does, go straight to return, do not pass 'go' and collect $200
520: if (str2.length() > upper) {
521: // the magic location int
522: int loc;
523:
524: // first we determine where the next space appears after lower
525: loc = str2.lastIndexOf(' ', upper);
526:
527: // now we'll see if the location is greater than the lower limit
528: if (loc >= lower) {
529: // yes it was, so we'll cut it off here
530: str2 = str2.substring(0, loc);
531: } else {
532: // no it wasnt, so we'll cut it off at the upper limit
533: str2 = str2.substring(0, upper);
534: loc = upper;
535: }
536:
537: // HTML was removed from original str
538: if (diff) {
539:
540: // location of last space in truncated string
541: loc = str2.lastIndexOf(' ', loc);
542:
543: // get last "word" in truncated string (add 1 to loc to eliminate space
544: String str3 = str2.substring(loc + 1);
545:
546: // find this fragment in original str, from 'loc' position
547: loc = str.indexOf(str3, loc) + str3.length();
548:
549: // get truncated string from original str, given new 'loc'
550: str2 = str.substring(0, loc);
551:
552: // get all the HTML from original str after loc
553: str3 = extractHTML(str.substring(loc));
554:
555: // remove any tags which generate visible HTML
556: // This call is unecessary, all HTML has already been stripped
557: //str3 = removeVisibleHTMLTags(str3);
558:
559: // append the appendToEnd String and
560: // add extracted HTML back onto truncated string
561: str = str2 + appendToEnd + str3;
562: } else {
563: // the string was truncated, so we append the appendToEnd String
564: str = str2 + appendToEnd;
565: }
566:
567: }
568:
569: return str;
570: }
571:
572: public static String truncateText(String str, int lower, int upper,
573: String appendToEnd) {
574: // strip markup from the string
575: String str2 = removeHTML(str, false);
576: boolean diff = (str2.length() < str.length());
577:
578: // quickly adjust the upper if it is set lower than 'lower'
579: if (upper < lower) {
580: upper = lower;
581: }
582:
583: // now determine if the string fits within the upper limit
584: // if it does, go straight to return, do not pass 'go' and collect $200
585: if (str2.length() > upper) {
586: // the magic location int
587: int loc;
588:
589: // first we determine where the next space appears after lower
590: loc = str2.lastIndexOf(' ', upper);
591:
592: // now we'll see if the location is greater than the lower limit
593: if (loc >= lower) {
594: // yes it was, so we'll cut it off here
595: str2 = str2.substring(0, loc);
596: } else {
597: // no it wasnt, so we'll cut it off at the upper limit
598: str2 = str2.substring(0, upper);
599: loc = upper;
600: }
601: // the string was truncated, so we append the appendToEnd String
602: str = str2 + appendToEnd;
603: }
604: return str;
605: }
606:
607: /**
608: * @param str
609: * @return
610: */
611: private static String stripLineBreaks(String str) {
612: // TODO: use a string buffer, ignore case !
613: str = str.replaceAll("<br>", "");
614: str = str.replaceAll("<br/>", "");
615: str = str.replaceAll("<br />", "");
616: str = str.replaceAll("<p></p>", "");
617: str = str.replaceAll("<p/>", "");
618: str = str.replaceAll("<p />", "");
619: return str;
620: }
621:
622: /**
623: * Need need to get rid of any user-visible HTML tags once all text has been
624: * removed such as <BR>. This sounds like a better approach than removing
625: * all HTML tags and taking the chance to leave some tags un-closed.
626: *
627: * WARNING: this method has serious performance problems a
628: *
629: * @author Alexis Moussine-Pouchkine <alexis.moussine-pouchkine@france.sun.com>
630: * @author Lance Lavandowska
631: * @param str the String object to modify
632: * @return the new String object without the HTML "visible" tags
633: */
634: private static String removeVisibleHTMLTags(String str) {
635: str = stripLineBreaks(str);
636: StringBuffer result = new StringBuffer(str);
637: StringBuffer lcresult = new StringBuffer(str.toLowerCase());
638:
639: // <img should take care of smileys
640: String[] visibleTags = { "<img" }; // are there others to add?
641: int stringIndex;
642: for (int j = 0; j < visibleTags.length; j++) {
643: while ((stringIndex = lcresult.indexOf(visibleTags[j])) != -1) {
644: if (visibleTags[j].endsWith(">")) {
645: result.delete(stringIndex, stringIndex
646: + visibleTags[j].length());
647: lcresult.delete(stringIndex, stringIndex
648: + visibleTags[j].length());
649: } else {
650: // need to delete everything up until next closing '>', for <img for instance
651: int endIndex = result.indexOf(">", stringIndex);
652: if (endIndex > -1) {
653: // only delete it if we find the end! If we don't the HTML may be messed up, but we
654: // can't safely delete anything.
655: result.delete(stringIndex, endIndex + 1);
656: lcresult.delete(stringIndex, endIndex + 1);
657: }
658: }
659: }
660: }
661:
662: // TODO: This code is buggy by nature. It doesn't deal with nesting of tags properly.
663: // remove certain elements with open & close tags
664: String[] openCloseTags = { "li", "a", "div", "h1", "h2", "h3",
665: "h4" }; // more ?
666: for (int j = 0; j < openCloseTags.length; j++) {
667: // could this be better done with a regular expression?
668: String closeTag = "</" + openCloseTags[j] + ">";
669: int lastStringIndex = 0;
670: while ((stringIndex = lcresult.indexOf("<"
671: + openCloseTags[j], lastStringIndex)) > -1) {
672: lastStringIndex = stringIndex;
673: // Try to find the matching closing tag (ignores possible nesting!)
674: int endIndex = lcresult.indexOf(closeTag, stringIndex);
675: if (endIndex > -1) {
676: // If we found it delete it.
677: result.delete(stringIndex, endIndex
678: + closeTag.length());
679: lcresult.delete(stringIndex, endIndex
680: + closeTag.length());
681: } else {
682: // Try to see if it is a self-closed empty content tag, i.e. closed with />.
683: endIndex = lcresult.indexOf(">", stringIndex);
684: int nextStart = lcresult.indexOf("<",
685: stringIndex + 1);
686: if (endIndex > stringIndex
687: && lcresult.charAt(endIndex - 1) == '/'
688: && (endIndex < nextStart || nextStart == -1)) {
689: // Looks like it, so remove it.
690: result.delete(stringIndex, endIndex + 1);
691: lcresult.delete(stringIndex, endIndex + 1);
692:
693: }
694: }
695: }
696: }
697:
698: return result.toString();
699: }
700:
701: /**
702: * Extract (keep) JUST the HTML from the String.
703: * @param str
704: * @return
705: */
706: public static String extractHTML(String str) {
707: if (str == null)
708: return "";
709: StringBuffer ret = new StringBuffer(str.length());
710: int start = 0;
711: int beginTag = str.indexOf("<");
712: int endTag = 0;
713: if (beginTag == -1)
714: return str;
715:
716: while (beginTag >= start) {
717: endTag = str.indexOf(">", beginTag);
718:
719: // if endTag found, keep tag
720: if (endTag > -1) {
721: ret.append(str.substring(beginTag, endTag + 1));
722:
723: // move start forward and find another tag
724: start = endTag + 1;
725: beginTag = str.indexOf("<", start);
726: }
727: // if no endTag found, break
728: else {
729: break;
730: }
731: }
732: return ret.toString();
733: }
734:
735: public static String hexEncode(String str) {
736: if (StringUtils.isEmpty(str))
737: return str;
738:
739: return RegexUtil.encode(str);
740: }
741:
742: public static String encodeEmail(String str) {
743: return str != null ? RegexUtil.encodeEmail(str) : null;
744: }
745:
746: /**
747: * URL encoding.
748: * @param s a string to be URL-encoded
749: * @return URL encoding of s using character encoding UTF-8; null if s is null.
750: */
751: public static final String encode(String s) {
752: try {
753: if (s != null)
754: return URLEncoder.encode(s, "UTF-8");
755: else
756: return s;
757: } catch (UnsupportedEncodingException e) {
758: // Java Spec requires UTF-8 be in all Java environments, so this should not happen
759: return s;
760: }
761: }
762:
763: /**
764: * URL decoding.
765: * @param s a URL-encoded string to be URL-decoded
766: * @return URL decoded value of s using character encoding UTF-8; null if s is null.
767: */
768: public static final String decode(String s) {
769: try {
770: if (s != null)
771: return URLDecoder.decode(s, "UTF-8");
772: else
773: return s;
774: } catch (UnsupportedEncodingException e) {
775: // Java Spec requires UTF-8 be in all Java environments, so this should not happen
776: return s;
777: }
778: }
779:
780: /**
781: * @param string
782: * @return
783: */
784: public static int stringToInt(String string) {
785: try {
786: return Integer.valueOf(string).intValue();
787: } catch (NumberFormatException e) {
788: mLogger.debug("Invalid Integer:" + string);
789: }
790: return 0;
791: }
792:
793: /**
794: * Convert a byte array into a Base64 string (as used in mime formats)
795: */
796: public static String toBase64(byte[] aValue) {
797:
798: final String m_strBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
799:
800: int byte1;
801: int byte2;
802: int byte3;
803: int iByteLen = aValue.length;
804: StringBuffer tt = new StringBuffer();
805:
806: for (int i = 0; i < iByteLen; i += 3) {
807: boolean bByte2 = (i + 1) < iByteLen;
808: boolean bByte3 = (i + 2) < iByteLen;
809: byte1 = aValue[i] & 0xFF;
810: byte2 = (bByte2) ? (aValue[i + 1] & 0xFF) : 0;
811: byte3 = (bByte3) ? (aValue[i + 2] & 0xFF) : 0;
812:
813: tt.append(m_strBase64Chars.charAt(byte1 / 4));
814: tt.append(m_strBase64Chars.charAt((byte2 / 16)
815: + ((byte1 & 0x3) * 16)));
816: tt.append(((bByte2) ? m_strBase64Chars.charAt((byte3 / 64)
817: + ((byte2 & 0xF) * 4)) : '='));
818: tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F)
819: : '='));
820: }
821:
822: return tt.toString();
823: }
824:
825: /**
826: * @param tag
827: * @return
828: */
829: public static String stripInvalidTagCharacters(String tag) {
830: if (tag == null)
831: throw new NullPointerException();
832:
833: StringBuffer sb = new StringBuffer();
834: char[] charArray = tag.toCharArray();
835: for (int i = 0; i < charArray.length; i++) {
836: char c = charArray[i];
837:
838: // fast-path exclusions quotes and commas are obvious
839: switch (c) {
840: case 34: // "
841: case 44: // ,
842: continue;
843: }
844:
845: if ((33 <= c && c <= 126)
846: || Character.isUnicodeIdentifierPart(c)
847: || Character.isUnicodeIdentifierStart(c)) {
848: sb.append(charArray[i]);
849: }
850: }
851: return sb.toString();
852: }
853:
854: public static String normalizeTag(String tag, Locale locale) {
855: tag = Utilities.stripInvalidTagCharacters(tag);
856: return locale == null ? tag.toLowerCase() : tag
857: .toLowerCase(locale);
858: }
859:
860: /**
861: * @param tags
862: * @return
863: */
864: public static List splitStringAsTags(String tags) {
865: String[] tagsarr = StringUtils.split(tags, TAG_SPLIT_CHARS);
866: if (tagsarr == null)
867: return Collections.EMPTY_LIST;
868: return Arrays.asList(tagsarr);
869: }
870: }
|