0001: /**
0002: * Copyright (c) 2003-2007, David A. Czarnecki
0003: * All rights reserved.
0004: *
0005: * Redistribution and use in source and binary forms, with or without
0006: * modification, are permitted provided that the following conditions are met:
0007: *
0008: * Redistributions of source code must retain the above copyright notice, this list of conditions and the
0009: * following disclaimer.
0010: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
0011: * following disclaimer in the documentation and/or other materials provided with the distribution.
0012: * Neither the name of "David A. Czarnecki" and "blojsom" nor the names of its contributors may be used to
0013: * endorse or promote products derived from this software without specific prior written permission.
0014: * Products derived from this software may not be called "blojsom", nor may "blojsom" appear in their name,
0015: * without prior written permission of David A. Czarnecki.
0016: *
0017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
0018: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
0019: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
0020: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
0021: * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
0022: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0025: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
0026: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
0027: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0028: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
0029: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0030: */package org.blojsom.util;
0031:
0032: import org.blojsom.blog.Blog;
0033: import org.blojsom.blog.Response;
0034:
0035: import javax.servlet.http.HttpServletRequest;
0036: import javax.servlet.http.HttpServletResponse;
0037: import java.io.*;
0038: import java.net.URLDecoder;
0039: import java.net.URLEncoder;
0040: import java.nio.ByteBuffer;
0041: import java.nio.channels.FileChannel;
0042: import java.security.MessageDigest;
0043: import java.security.NoSuchAlgorithmException;
0044: import java.text.Collator;
0045: import java.text.SimpleDateFormat;
0046: import java.util.*;
0047: import java.util.regex.Pattern;
0048: import java.util.regex.Matcher;
0049:
0050: /**
0051: * BlojsomUtils
0052: *
0053: * @author David Czarnecki
0054: * @since blojsom 3.0
0055: * @version $Id: BlojsomUtils.java,v 1.16 2007/01/21 15:42:48 czarneckid Exp $
0056: */
0057: public class BlojsomUtils implements BlojsomConstants {
0058:
0059: private static final int REGEX_OPTIONS = Pattern.DOTALL
0060: | Pattern.CASE_INSENSITIVE;
0061: private static final Pattern STRIP_HTML_PATTERN = Pattern.compile(
0062: "^[^<>]*>|<.*?>|<[^<>]*$", REGEX_OPTIONS);
0063:
0064: /**
0065: * Private constructor so that the class cannot be instantiated.
0066: */
0067: private BlojsomUtils() {
0068: }
0069:
0070: /**
0071: * Filter only directories
0072: */
0073: private static final FileFilter DIRECTORY_FILTER = new FileFilter() {
0074:
0075: /**
0076: * Tests whether or not the specified abstract pathname should be
0077: * included in a pathname list.
0078: *
0079: * @param pathname The abstract pathname to be tested
0080: * @return <code>true</code> if and only if <code>pathname</code>
0081: * should be included
0082: */
0083: public boolean accept(File pathname) {
0084: return (pathname.isDirectory());
0085: }
0086: };
0087:
0088: /**
0089: * Filter only files
0090: */
0091: private static final FileFilter FILE_FILTER = new FileFilter() {
0092:
0093: /**
0094: * Tests whether or not the specified abstract pathname should be
0095: * included in a pathname list.
0096: *
0097: * @param pathname The abstract pathname to be tested
0098: * @return <code>true</code> if and only if <code>pathname</code>
0099: * should be included
0100: */
0101: public boolean accept(File pathname) {
0102: return (!pathname.isDirectory());
0103: }
0104: };
0105:
0106: /**
0107: * RFC-822 format
0108: * SimpleDateFormats are not threadsafe, but we should not need more than one per
0109: * thread.
0110: */
0111: private static final ThreadLocal RFC_822_DATE_FORMAT_OBJECT = new ThreadLocal() {
0112: protected Object initialValue() {
0113: return new SimpleDateFormat(RFC_822_DATE_FORMAT, Locale.US);
0114: }
0115: };
0116:
0117: /**
0118: * ISO-8601 format
0119: * SimpleDateFormats are not threadsafe, but we should not need more than one per
0120: * thread.
0121: */
0122: private static final ThreadLocal ISO_8601_DATE_FORMAT_OBJECT = new ThreadLocal() {
0123: protected Object initialValue() {
0124: SimpleDateFormat sdf = new SimpleDateFormat(
0125: ISO_8601_DATE_FORMAT);
0126: sdf.getTimeZone().setID("+00:00");
0127: return sdf;
0128: }
0129: };
0130:
0131: /**
0132: * UTC format
0133: * SimpleDateFormats are not threadsafe, but we should not need more than one per
0134: * thread.
0135: */
0136: private static final ThreadLocal UTC_DATE_FORMAT_OBJECT = new ThreadLocal() {
0137: protected Object initialValue() {
0138: return new SimpleDateFormat(UTC_DATE_FORMAT);
0139: }
0140: };
0141:
0142: /**
0143: * Return a file filter which only returns directories
0144: *
0145: * @return File filter appropriate for filtering only directories
0146: */
0147: public static FileFilter getDirectoryFilter() {
0148: return DIRECTORY_FILTER;
0149: }
0150:
0151: /**
0152: * Return a file filter which only returns directories that are not one of a list
0153: * of excluded directories
0154: *
0155: * @param excludedDirectories List of directories to exclude
0156: * @return File filter appropriate for filtering only directories
0157: */
0158: public static FileFilter getDirectoryFilter(
0159: final String[] excludedDirectories) {
0160: if (excludedDirectories == null) {
0161: return DIRECTORY_FILTER;
0162: }
0163:
0164: return new FileFilter() {
0165: public boolean accept(File pathname) {
0166: if (!pathname.isDirectory()) {
0167: return false;
0168: } else {
0169: for (int i = 0; i < excludedDirectories.length; i++) {
0170: String excludedDirectory = excludedDirectories[i];
0171: if (pathname.toString().matches(
0172: excludedDirectory)) {
0173: return false;
0174: }
0175: }
0176: }
0177: return true;
0178: }
0179: };
0180: }
0181:
0182: /**
0183: * Return a date in RFC 822 style
0184: *
0185: * @param date Date
0186: * @return Date formatted as RFC 822
0187: */
0188: public static String getRFC822Date(Date date) {
0189: return ((SimpleDateFormat) RFC_822_DATE_FORMAT_OBJECT.get())
0190: .format(date);
0191: }
0192:
0193: /**
0194: * Return a date formatted date
0195: *
0196: * @param date Date
0197: * @param format Date Format String
0198: * @param locale Locale Locale for retrieving proper date symbols
0199: * @return Date formatted date
0200: */
0201: public static String getFormattedDate(Date date, String format,
0202: Locale locale) {
0203: SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
0204: return sdf.format(date);
0205: }
0206:
0207: /**
0208: * Return a date in ISO 8601 style
0209: * http://www.w3.org/TR/NOTE-datetime
0210: *
0211: * @param date Date
0212: * @return Date formatted as ISO 8601
0213: */
0214: public static String getISO8601Date(Date date) {
0215: return ((SimpleDateFormat) ISO_8601_DATE_FORMAT_OBJECT.get())
0216: .format(date).replaceAll("GMT", "");
0217: }
0218:
0219: /**
0220: * Return a date in UTC style
0221: *
0222: * @param date Date
0223: * @return Date formatted as ISO 8601
0224: */
0225: public static String getUTCDate(Date date) {
0226: return ((SimpleDateFormat) UTC_DATE_FORMAT_OBJECT.get())
0227: .format(date);
0228: }
0229:
0230: /**
0231: * Return a file filter which takes a list of regular expressions to look for
0232: *
0233: * @param expressions List of regular expressions for files to retrieve
0234: * @return File filter appropriate for filtering out a set of files based on regular expressions
0235: */
0236: public static FileFilter getRegularExpressionFilter(
0237: final String[] expressions) {
0238: return new FileFilter() {
0239:
0240: private Date today = new Date();
0241:
0242: public boolean accept(File pathname) {
0243: if (pathname.isDirectory()) {
0244: return false;
0245: }
0246:
0247: for (int i = 0; i < expressions.length; i++) {
0248: String expression = expressions[i];
0249: if (pathname.getName().matches(expression)) {
0250: return pathname.lastModified() <= today
0251: .getTime();
0252: }
0253: }
0254:
0255: return false;
0256: }
0257: };
0258: }
0259:
0260: /**
0261: * Return a file filter which takes a list of file extensions to look for
0262: *
0263: * @param extensions List of file extensions
0264: * @return File filter appropriate for filtering out a set of file extensions
0265: */
0266: public static FileFilter getExtensionsFilter(
0267: final String[] extensions) {
0268: return new FileFilter() {
0269: public boolean accept(File pathname) {
0270: if (pathname.isDirectory()) {
0271: return false;
0272: }
0273:
0274: for (int i = 0; i < extensions.length; i++) {
0275: String extension = extensions[i];
0276: if (pathname.getName().endsWith(extension)) {
0277: return true;
0278: }
0279: }
0280: return false;
0281: }
0282: };
0283: }
0284:
0285: /**
0286: * Return a file filter which takes a list of file extensions to look for
0287: *
0288: * @param extensions List of file extensions
0289: * @param excludedDirectories List of excluded directories
0290: * @param returnDirectories Whether or not to return
0291: * @return File filter appropriate for filtering out a set of file extensions
0292: */
0293: public static FileFilter getExtensionsFilter(
0294: final String[] extensions,
0295: final String[] excludedDirectories,
0296: final boolean returnDirectories) {
0297: return new FileFilter() {
0298: public boolean accept(File pathname) {
0299: if (pathname.isDirectory() && returnDirectories) {
0300: String path = pathname.toString();
0301:
0302: for (int i = 0; i < excludedDirectories.length; i++) {
0303: String excludedDirectory = excludedDirectories[i];
0304: if (path.matches(excludedDirectory)) {
0305: return false;
0306: }
0307: }
0308:
0309: return true;
0310: }
0311:
0312: for (int i = 0; i < extensions.length; i++) {
0313: String extension = extensions[i];
0314: if (pathname.getName().matches(extension)) {
0315: return true;
0316: }
0317: }
0318:
0319: return false;
0320: }
0321: };
0322: }
0323:
0324: /**
0325: * Return a file filter which takes a single file extension to look for
0326: *
0327: * @param extension File extension
0328: * @return File filter appropriate for filtering out a single file extension
0329: */
0330: public static FileFilter getExtensionFilter(final String extension) {
0331: return getExtensionsFilter(new String[] { extension });
0332: }
0333:
0334: /**
0335: * Parse a comma-separated list of values; also parses over internal spaces
0336: *
0337: * @param commaList Comma-separated list
0338: * @return Individual strings from the comma-separated list
0339: */
0340: public static String[] parseCommaList(String commaList) {
0341: return parseDelimitedList(commaList, ", ");
0342: }
0343:
0344: /**
0345: * Parse a comma-separated list of values
0346: *
0347: * @param commaList Comma-separated list
0348: * @return Individual strings from the comma-separated list
0349: */
0350: public static String[] parseOnlyCommaList(String commaList) {
0351: return parseOnlyCommaList(commaList, false);
0352: }
0353:
0354: /**
0355: * Parse a comma-separated list of values
0356: *
0357: * @param commaList Comma-separated list
0358: * @param trim If the contents of the array should be trimmed
0359: * @return Individual strings from the comma-separated list
0360: */
0361: public static String[] parseOnlyCommaList(String commaList,
0362: boolean trim) {
0363: return parseDelimitedList(commaList, ",", trim);
0364: }
0365:
0366: /**
0367: * Parse a string into two separate strings based on the last comma in the input value
0368: *
0369: * @param value Input
0370: * @return Parsed string
0371: */
0372: public static String[] parseLastComma(String value) {
0373: if (checkNullOrBlank(value)) {
0374: return new String[] { value };
0375: }
0376:
0377: int lastCommaIndex = value.lastIndexOf(",");
0378:
0379: if (lastCommaIndex == -1) {
0380: return new String[] { value };
0381: } else {
0382: return new String[] { value.substring(0, lastCommaIndex),
0383: value.substring(lastCommaIndex + 1) };
0384: }
0385: }
0386:
0387: /**
0388: * Parse a delimited list of values
0389: *
0390: * @param delimitedList Delimited list
0391: * @param delimiter Field Delimiter
0392: * @return Individual strings from the comma-separated list
0393: */
0394: public static String[] parseDelimitedList(String delimitedList,
0395: String delimiter) {
0396: return parseDelimitedList(delimitedList, delimiter, false);
0397: }
0398:
0399: /**
0400: * Parse a delimited list of values
0401: *
0402: * @param delimitedList Delimited list
0403: * @param delimiter Field Delimiter
0404: * @param trim If the contents of the array should be trimmed
0405: * @return Individual strings from the comma-separated list
0406: */
0407: public static String[] parseDelimitedList(String delimitedList,
0408: String delimiter, boolean trim) {
0409: if (delimitedList == null) {
0410: return null;
0411: }
0412:
0413: StringTokenizer tokenizer = new StringTokenizer(delimitedList,
0414: delimiter);
0415: ArrayList list = new ArrayList();
0416: while (tokenizer.hasMoreTokens()) {
0417: if (trim) {
0418: list.add(tokenizer.nextToken().trim());
0419: } else {
0420: list.add(tokenizer.nextToken());
0421: }
0422: }
0423:
0424: if (list.size() == 0) {
0425: return new String[] {};
0426: }
0427:
0428: return (String[]) list.toArray(new String[list.size()]);
0429: }
0430:
0431: /**
0432: * Convert the request parameters to a string
0433: *
0434: * @param request Servlet request
0435: * @return Request parameters in the form &name=value
0436: */
0437: public static String convertRequestParams(HttpServletRequest request) {
0438: Enumeration paramNames = request.getParameterNames();
0439: StringBuffer buffer = new StringBuffer();
0440: while (paramNames.hasMoreElements()) {
0441: String name = (String) paramNames.nextElement();
0442: String value = request.getParameter(name);
0443: try {
0444: buffer.append(URLEncoder.encode(name, UTF8))
0445: .append("=").append(
0446: URLEncoder.encode(value, UTF8));
0447: } catch (UnsupportedEncodingException e) {
0448: }
0449: if (paramNames.hasMoreElements()) {
0450: buffer.append("&");
0451: }
0452: }
0453: return buffer.toString();
0454: }
0455:
0456: /**
0457: * Convert the request parameters to a string
0458: *
0459: * @param request Servlet request
0460: * @param ignoreParams Parameters to ignore when converting the request
0461: * @return Request parameters in the form &name=value
0462: */
0463: public static String convertRequestParams(
0464: HttpServletRequest request, Map ignoreParams) {
0465: Enumeration paramNames = request.getParameterNames();
0466: StringBuffer buffer = new StringBuffer();
0467: while (paramNames.hasMoreElements()) {
0468: String name = (String) paramNames.nextElement();
0469: String value = request.getParameter(name);
0470: //noinspection EmptyCatchBlock
0471: try {
0472: if (!ignoreParams.containsKey(name)) {
0473: buffer.append(URLEncoder.encode(name, UTF8))
0474: .append("=").append(
0475: URLEncoder.encode(value, UTF8))
0476: .append("&");
0477: }
0478: } catch (UnsupportedEncodingException e) {
0479: }
0480: }
0481:
0482: return buffer.toString();
0483: }
0484:
0485: /**
0486: * Return a URL to the main blog site without the servlet path requested
0487: *
0488: * @param blogURL URL for the blog
0489: * @param servletPath Servlet path under which the blog is placed
0490: * @return URL to the blog up to the servlet path
0491: */
0492: public static String getBlogSiteURL(String blogURL,
0493: String servletPath) {
0494: if (servletPath == null || "".equals(servletPath)) {
0495: return blogURL;
0496: }
0497: int servletPathIndex = blogURL.indexOf(servletPath, 7);
0498: if (servletPathIndex == -1) {
0499: return blogURL;
0500: }
0501:
0502: return blogURL.substring(0, servletPathIndex);
0503: }
0504:
0505: /**
0506: * Return an escaped string where &, <, >, ", and ' are converted to their HTML equivalents
0507: *
0508: * @param input Unescaped string
0509: * @return Escaped string containing HTML equivalents for &, <, >, ", and '
0510: */
0511: public static String escapeString(String input) {
0512: if (input == null) {
0513: return null;
0514: }
0515:
0516: String unescaped = replace(input, "&", "&");
0517: unescaped = replace(unescaped, "<", "<");
0518: unescaped = replace(unescaped, ">", ">");
0519: unescaped = replace(unescaped, "\"", """);
0520: unescaped = replace(unescaped, "'", "'");
0521:
0522: return unescaped;
0523: }
0524:
0525: /**
0526: * Return an escaped string where &, <, > are converted to their HTML equivalents
0527: *
0528: * @param input Unescaped string
0529: * @return Escaped string containing HTML equivalents for &, <, >
0530: */
0531: public static String escapeStringSimple(String input) {
0532: if (input == null) {
0533: return null;
0534: }
0535:
0536: String unescaped = replace(input, "&", "&");
0537: unescaped = replace(unescaped, "<", "<");
0538: unescaped = replace(unescaped, ">", ">");
0539:
0540: return unescaped;
0541: }
0542:
0543: /**
0544: * Return an escaped string where <, > are converted to their HTML equivalents
0545: *
0546: * @param input Unescaped string
0547: * @return Escaped string containing HTML equivalents for <, >
0548: */
0549: public static String escapeBrackets(String input) {
0550: if (input == null) {
0551: return null;
0552: }
0553:
0554: String unescaped = replace(input, "<", "<");
0555: unescaped = replace(unescaped, ">", ">");
0556:
0557: return unescaped;
0558: }
0559:
0560: /**
0561: * Return an escaped string where <meta, <link tags are escaped
0562: *
0563: * @param input Unescaped string
0564: * @return Escaped string where <meta, <link tags are escaped
0565: */
0566: public static String escapeMetaAndLink(String input) {
0567: if (input == null) {
0568: return null;
0569: }
0570:
0571: String cleanedInput = input.replaceAll("<[mM][eE][tT][aA]",
0572: "<meta");
0573: cleanedInput = cleanedInput.replaceAll("<[lL][iI][nN][kK]",
0574: "<link");
0575: return cleanedInput;
0576: }
0577:
0578: /**
0579: * Replace any occurances of a string pattern within a string with a different string.
0580: *
0581: * @param str The source string. This is the string that will be searched and have the replacements
0582: * @param pattern The pattern to look for in str
0583: * @param replace The string to insert in the place of <i>pattern</i>
0584: * @return String with replace occurences
0585: */
0586: public static String replace(String str, String pattern,
0587: String replace) {
0588: if (str == null || "".equals(str)) {
0589: return str;
0590: }
0591:
0592: if (replace == null) {
0593: return str;
0594: }
0595:
0596: if ("".equals(pattern)) {
0597: return str;
0598: }
0599:
0600: int s = 0;
0601: int e;
0602: StringBuffer result = new StringBuffer();
0603:
0604: while ((e = str.indexOf(pattern, s)) >= 0) {
0605: result.append(str.substring(s, e));
0606: result.append(replace);
0607: s = e + pattern.length();
0608: }
0609: result.append(str.substring(s));
0610: return result.toString();
0611: }
0612:
0613: /**
0614: * Return the file extension for a given filename or <code>null</code> if no file extension
0615: * is present
0616: *
0617: * @param filename Filename
0618: * @return File extension without the . or <code>null</code> if no file extension is present
0619: */
0620: public static String getFileExtension(String filename) {
0621: if (filename == null) {
0622: return null;
0623: }
0624:
0625: int dotIndex = filename.lastIndexOf(".");
0626: if (dotIndex == -1) {
0627: return null;
0628: } else {
0629: return filename.substring(dotIndex + 1);
0630: }
0631: }
0632:
0633: /**
0634: * Return the filename without extension for a given filename
0635: *
0636: * @param filename Filename
0637: * @return Filename up to the .
0638: */
0639: public static String getFilename(String filename) {
0640: int dotIndex = filename.lastIndexOf(".");
0641: if (dotIndex == -1) {
0642: return filename;
0643: } else {
0644: return filename.substring(0, dotIndex);
0645: }
0646: }
0647:
0648: /**
0649: * Returns the base file name from the supplied file path. On the surface,
0650: * this would appear to be a trivial task. Apparently, however, some Linux
0651: * JDKs do not implement <code>File.getName()</code> correctly for Windows
0652: * paths, so we attempt to take care of that here.
0653: *
0654: * @param filenameWithPath The full path to the file.
0655: * @return The base file name, from the end of the path.
0656: */
0657: public static String getFilenameFromPath(String filenameWithPath) {
0658: // First, ask the JDK for the base file name.
0659: String fileName = new File(filenameWithPath).getName();
0660:
0661: // Now check for a Windows file name parsed incorrectly.
0662: int colonIndex = fileName.indexOf(":");
0663: if (colonIndex == -1) {
0664: // Check for a Windows SMB file path.
0665: colonIndex = fileName.indexOf("\\\\");
0666: }
0667: int backslashIndex = fileName.lastIndexOf("\\");
0668:
0669: if (colonIndex > -1 && backslashIndex > -1) {
0670: // Consider this filename to be a full Windows path, and parse it
0671: // accordingly to retrieve just the base file name.
0672: fileName = fileName.substring(backslashIndex + 1);
0673: }
0674:
0675: return fileName;
0676: }
0677:
0678: /**
0679: * Return a string of "YYYYMMDD"
0680: *
0681: * @param date Date from which to extract "key"
0682: * @return String of "YYYYMMDD"
0683: */
0684: public static String getDateKey(Date date) {
0685: StringBuffer value = new StringBuffer();
0686: Calendar calendar = Calendar.getInstance();
0687: long l;
0688:
0689: calendar.setTime(date);
0690: value.append(calendar.get(Calendar.YEAR));
0691: // month and date need to be 2 digits; otherwise it is
0692: // impossible to distinguish between e.g. November (11)
0693: // and January (1) when using the date as a prefix
0694: l = calendar.get(Calendar.MONTH) + 1;
0695: if (l < 10) {
0696: value.append("0");
0697: }
0698: value.append(l);
0699: l = calendar.get(Calendar.DAY_OF_MONTH);
0700: if (l < 10) {
0701: value.append("0");
0702: }
0703: value.append(l);
0704: // highest possible values above are 12 and 31, so no need to
0705: // be generic & handle arbitrary-length digits
0706:
0707: return value.toString();
0708: }
0709:
0710: /**
0711: * Remove the initial "/" from a string
0712: *
0713: * @param input Input string
0714: * @return Input string without initial "/" removed or <code>null</code> if the input was null
0715: */
0716: public static String removeInitialSlash(String input) {
0717: if (input == null) {
0718: return null;
0719: }
0720:
0721: if (!input.startsWith("/")) {
0722: return input;
0723: } else {
0724: return input.substring(1);
0725: }
0726: }
0727:
0728: /**
0729: * Remove the trailing "/" from a string
0730: *
0731: * @param input Input string
0732: * @return Input string with trailing "/" removed or <code>null</code> if the input was null
0733: */
0734: public static String removeTrailingSlash(String input) {
0735: if (input == null) {
0736: return null;
0737: }
0738:
0739: if (!input.endsWith("/")) {
0740: return input;
0741: } else {
0742: return input.substring(0, input.length() - 1);
0743: }
0744: }
0745:
0746: /**
0747: * Remove the "/" from the beginning and end of a string
0748: *
0749: * @param input Input string
0750: * @return Input string with beginning and ending "/" removed or <code>null</code> if the input was null
0751: */
0752: public static String removeSlashes(String input) {
0753: input = removeInitialSlash(input);
0754: input = removeTrailingSlash(input);
0755: return input;
0756: }
0757:
0758: /**
0759: * Extracts the first line in a given string, otherwise returns the first n bytes
0760: *
0761: * @param input String from which to extract the first line
0762: * @param length Number of bytes to return if line seperator isnot found
0763: * @return the first line of the string
0764: */
0765: public static String getFirstLine(String input, int length) {
0766: String result;
0767: String lineSeparator = LINE_SEPARATOR;
0768: int titleIndex = input.indexOf(lineSeparator);
0769: if (titleIndex == -1) {
0770: result = input.substring(0, length) + "...";
0771: } else {
0772: result = input.substring(0, titleIndex);
0773: }
0774: return result;
0775: }
0776:
0777: /**
0778: * Return the template name for a particular page
0779: *
0780: * @param flavorTemplate Flavor template filename
0781: * @param page Requested page
0782: * @return Return an appropriate template name for the flavor template and page combination
0783: */
0784: public static String getTemplateForPage(String flavorTemplate,
0785: String page) {
0786: int dotIndex = flavorTemplate.lastIndexOf(".");
0787: if (dotIndex == -1) {
0788: return flavorTemplate + '-' + page;
0789: } else {
0790: StringBuffer newTemplate = new StringBuffer();
0791: if (page.startsWith("/")) {
0792: newTemplate.append(removeInitialSlash(page));
0793: } else {
0794: newTemplate.append(flavorTemplate
0795: .substring(0, dotIndex));
0796: newTemplate.append("-");
0797: newTemplate.append(page);
0798: }
0799: newTemplate.append(".");
0800: newTemplate.append(flavorTemplate.substring(dotIndex + 1,
0801: flavorTemplate.length()));
0802: return newTemplate.toString();
0803: }
0804: }
0805:
0806: /**
0807: * Tries to retrieve a given key using getParameter(key) and if not available, will
0808: * use getAttribute(key) from the servlet request
0809: *
0810: * @param key Parameter to retrieve
0811: * @param httpServletRequest Request
0812: * @return Value of the key as a string, or <code>null</code> if there is no parameter/attribute
0813: */
0814: public static String getRequestValue(String key,
0815: HttpServletRequest httpServletRequest) {
0816: return getRequestValue(key, httpServletRequest, false);
0817: }
0818:
0819: /**
0820: * Tries to retrieve a given key using getParameter(key) and if not available, will
0821: * use getAttribute(key) from the servlet request
0822: *
0823: * @param key Parameter to retrieve
0824: * @param httpServletRequest Request
0825: * @param preferAttributes If request attributes should be checked before request parameters
0826: * @return Value of the key as a string, or <code>null</code> if there is no parameter/attribute
0827: */
0828: public static String getRequestValue(String key,
0829: HttpServletRequest httpServletRequest,
0830: boolean preferAttributes) {
0831: if (!preferAttributes) {
0832: if (httpServletRequest.getParameter(key) != null) {
0833: return httpServletRequest.getParameter(key);
0834: } else if (httpServletRequest.getAttribute(key) != null) {
0835: return httpServletRequest.getAttribute(key).toString();
0836: }
0837: } else {
0838: if (httpServletRequest.getAttribute(key) != null) {
0839: return httpServletRequest.getAttribute(key).toString();
0840: } else if (httpServletRequest.getParameter(key) != null) {
0841: return httpServletRequest.getParameter(key);
0842: }
0843: }
0844:
0845: return null;
0846: }
0847:
0848: /**
0849: * Get request values for a given key and if not available, returns and empty <code>String[]</code>
0850: *
0851: * @param key Parameter to retrieve
0852: * @param httpServletRequest Request
0853: * @return Request values for the key as a <code>String[]</code>
0854: */
0855: public static String[] getRequestValues(String key,
0856: HttpServletRequest httpServletRequest) {
0857: String[] values = httpServletRequest.getParameterValues(key);
0858:
0859: if (values == null) {
0860: values = new String[0];
0861: }
0862:
0863: return values;
0864: }
0865:
0866: /**
0867: * Return only the filename of a permalink request
0868: *
0869: * @param permalink Permalink request
0870: * @param blogEntryExtensions Regex for blog entries so that we only pickup requests for valid blog entries
0871: * @return Filename portion of permalink request
0872: */
0873: public static String getFilenameForPermalink(String permalink,
0874: String[] blogEntryExtensions) {
0875: if (permalink == null) {
0876: return null;
0877: }
0878:
0879: boolean matchesExtension = false;
0880: for (int i = 0; i < blogEntryExtensions.length; i++) {
0881: String blogEntryExtension = blogEntryExtensions[i];
0882: if (permalink.matches(blogEntryExtension)) {
0883: matchesExtension = true;
0884: break;
0885: }
0886: }
0887:
0888: if (!matchesExtension) {
0889: return null;
0890: }
0891:
0892: int indexOfSlash = permalink.lastIndexOf("/");
0893: if (indexOfSlash == -1) {
0894: indexOfSlash = permalink.lastIndexOf("\\");
0895: }
0896:
0897: if (indexOfSlash == -1) {
0898: return permalink;
0899: } else {
0900: String sanitizedPermalink = permalink.substring(
0901: indexOfSlash + 1, permalink.length());
0902: if (sanitizedPermalink.startsWith("..")) {
0903: sanitizedPermalink = sanitizedPermalink.substring(2,
0904: sanitizedPermalink.length());
0905: } else if (sanitizedPermalink.startsWith(".")) {
0906: sanitizedPermalink = sanitizedPermalink.substring(1,
0907: sanitizedPermalink.length());
0908: }
0909:
0910: return sanitizedPermalink;
0911: }
0912: }
0913:
0914: /**
0915: * Return an input string URL encoded
0916: *
0917: * @param input Input string
0918: * @return URL encoded string, <code>null</code> if the input was null,
0919: * or <code>input</code> unmodified there is an encoding exception
0920: */
0921: public static String urlEncode(String input) {
0922: if (input == null) {
0923: return null;
0924: }
0925:
0926: try {
0927: return URLEncoder.encode(input, UTF8);
0928: } catch (UnsupportedEncodingException e) {
0929: return input;
0930: }
0931: }
0932:
0933: /**
0934: * Return an input string URL encoded for a URL link where '/' show as '/'
0935: *
0936: * @param input Input string
0937: * @return URL encoded string, <code>null</code> if the input was null,
0938: * or <code>input</code> unmodified there is an encoding exception
0939: */
0940: public static String urlEncodeForLink(String input) {
0941: if (input == null) {
0942: return null;
0943: }
0944:
0945: try {
0946: String result = URLEncoder.encode(input, UTF8);
0947: result = replace(result, "%2F", "/");
0948: result = replace(result, "%20", "+");
0949: result = replace(result, "%3A", ":");
0950: result = replace(result, "%3a", ":");
0951: return result;
0952: } catch (UnsupportedEncodingException e) {
0953: return input;
0954: }
0955: }
0956:
0957: /**
0958: * Return a URL decoded string
0959: *
0960: * @param input Input string
0961: * @return URL decoded string or <code>null</code> if either the input was null or there is a decoding exception
0962: */
0963: public static String urlDecode(String input) {
0964: if (input == null) {
0965: return null;
0966: }
0967:
0968: try {
0969: return URLDecoder.decode(input, UTF8);
0970: } catch (UnsupportedEncodingException e) {
0971: return null;
0972: }
0973: }
0974:
0975: /**
0976: * Create a Calendar Navigatation URL
0977: *
0978: * @param prefix Any URL Prefix
0979: * @param month Month of navigation
0980: * @param day Day of navigation
0981: * @param year Year of navigation
0982: * @return Properly formatted calendar navigation url
0983: */
0984: public static String getCalendarNavigationUrl(String prefix,
0985: int month, int day, int year) {
0986: StringBuffer dateurl = new StringBuffer(prefix);
0987:
0988: if (year != -1) {
0989: dateurl.append(year).append("/");
0990: }
0991:
0992: if (month != -1) {
0993: if (month < 10) {
0994: dateurl.append("0");
0995: }
0996:
0997: dateurl.append(month).append("/");
0998: }
0999:
1000: if (day != -1) {
1001: if (day < 10) {
1002: dateurl.append("0");
1003: }
1004:
1005: dateurl.append(day).append("/");
1006: }
1007:
1008: return dateurl.toString();
1009: }
1010:
1011: /**
1012: * Return a comparator to sort by name
1013: */
1014: public static final Comparator FILE_NAME_COMPARATOR = new Comparator() {
1015: public int compare(Object o1, Object o2) {
1016: String s1 = (String) o1;
1017: String s2 = (String) o2;
1018:
1019: return s1.compareTo(s2);
1020: }
1021: };
1022:
1023: static final byte[] HEX_DIGITS = { (byte) '0', (byte) '1',
1024: (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
1025: (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b',
1026: (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
1027:
1028: /**
1029: * Performs an MD5 Digest onthe given String content
1030: *
1031: * @param data Content to digest
1032: * @return The Hash as Hex String
1033: */
1034: public static String digestString(String data) {
1035: return digestString(data, DEFAULT_DIGEST_ALGORITHM);
1036: }
1037:
1038: /**
1039: * Performs an Digest onthe given String content for the given algorithm
1040: *
1041: * @param data Content to digest
1042: * @param algorithm the algorithm to use (MD5, SHA1)
1043: * @return The Hash as Hex String
1044: */
1045: public static String digestString(String data, String algorithm) {
1046: String result = null;
1047: if (data != null) {
1048: try {
1049: MessageDigest _md = MessageDigest
1050: .getInstance(algorithm);
1051: _md.update(data.getBytes());
1052: byte[] _digest = _md.digest();
1053: String _ds;
1054: _ds = toHexString(_digest, 0, _digest.length);
1055: result = _ds;
1056: } catch (NoSuchAlgorithmException e) {
1057: result = null;
1058: }
1059: }
1060: return result;
1061: }
1062:
1063: /**
1064: * Convert Byte Array to Hex Value
1065: *
1066: * @param buf Byte Array to convert to Hex Value
1067: * @param offset Starting Offset for Conversion
1068: * @param length Length to convery
1069: * @param value Hex Value
1070: */
1071: private static void toHexValue(byte[] buf, int offset, int length,
1072: int value) {
1073: do {
1074: buf[offset + --length] = HEX_DIGITS[value & 0x0f];
1075: value >>>= 4;
1076: } while (value != 0 && length > 0);
1077:
1078: while (--length >= 0) {
1079: buf[offset + length] = HEX_DIGITS[0];
1080: }
1081: }
1082:
1083: /**
1084: * Convert a byte array to a hex string
1085: *
1086: * @param buf Byte array to convert to hex string
1087: * @param offset Starting offset for conversion
1088: * @param length Length to convert
1089: * @return Hex string representing the byte array
1090: */
1091: public static String toHexString(byte[] buf, int offset, int length) {
1092: byte[] buf1 = new byte[length * 2];
1093: for (int i = 0; i < length; i++) {
1094: toHexValue(buf1, i * 2, 2, buf[i + offset]);
1095: }
1096: return new String(buf1);
1097: }
1098:
1099: /**
1100: * Normalize a path to remove all ./, ../, .../, //, etc. type references
1101: *
1102: * @param path Input path
1103: * @return Normalized path
1104: */
1105: public static String normalize(String path) {
1106: if (path == null) {
1107: return null;
1108: }
1109:
1110: String value = path;
1111: value = value.replaceAll("\\.*", "");
1112: value = value.replaceAll("/{2,}", "");
1113: return value;
1114: }
1115:
1116: /**
1117: * Check to see if the given input string is <code>null</code> and if so, return a blank string instead
1118: *
1119: * @param input Input string
1120: * @return Blank string if the input string is <code>null</code>, otherwise just return the input string
1121: */
1122: public static String nullToBlank(String input) {
1123: return (input == null) ? "" : input;
1124: }
1125:
1126: /**
1127: * Convert a set of {@link Properties} to a {@link Map}
1128: *
1129: * @param properties Properties to be converted to a Map
1130: * @return Map object containing all the keys and values from the original Properties object. If the
1131: * Properties object was null, a new Map is returned with no values.
1132: */
1133: public static Map propertiesToMap(Properties properties) {
1134: if (properties == null) {
1135: return new HashMap();
1136: } else {
1137: Iterator keyIterator = properties.keySet().iterator();
1138: Object key;
1139: Object value;
1140: HashMap convertedProperties = new HashMap();
1141: while (keyIterator.hasNext()) {
1142: key = keyIterator.next();
1143: value = properties.get(key);
1144: convertedProperties.put(key, value);
1145: }
1146:
1147: return convertedProperties;
1148: }
1149: }
1150:
1151: /**
1152: * Convert a {@link Properties} object to a {@link Map}. If the properties object is <code>null</code>
1153: * an emtpy {@link Map} is returned.
1154: *
1155: * @param properties {@link Properties}
1156: * @return {@link Map} containing keys and values from the properties
1157: */
1158: public static Map blojsomPropertiesToMap(Properties properties) {
1159: if (properties == null) {
1160: return new HashMap();
1161: } else {
1162: Iterator keyIterator = properties.keySet().iterator();
1163: Object key;
1164: Object value;
1165: HashMap convertedProperties = new HashMap();
1166: while (keyIterator.hasNext()) {
1167: key = keyIterator.next();
1168: value = properties.get(key);
1169: if (value instanceof List) {
1170: convertedProperties.put(key, value);
1171: } else {
1172: ArrayList values = new ArrayList();
1173: values.add(value.toString());
1174: convertedProperties.put(key, values);
1175: }
1176: }
1177:
1178: return convertedProperties;
1179: }
1180: }
1181:
1182: /**
1183: * Turn an array of strings into a single string separated by a given delimeter. If the incoming array is null, this
1184: * method returns the <code>null</code> string.
1185: *
1186: * @param array Array of strings
1187: * @param separator Separator between strings
1188: * @return Single string containing all the strings from the original array separated by the given delimeter, or <code>null</code> if the input was null.
1189: */
1190: public static String arrayOfStringsToString(String[] array,
1191: String separator) {
1192: if (array == null) {
1193: return null;
1194: }
1195:
1196: StringBuffer result = new StringBuffer();
1197: if (array.length > 0) {
1198: result.append(array[0]);
1199: // now loop over the rest of the array, appending separators first
1200: for (int i = 1; i < array.length; i++) {
1201: result.append(separator);
1202: result.append(array[i]);
1203: }
1204: }
1205:
1206: return result.toString();
1207: }
1208:
1209: /**
1210: * Turn an array of strings into a single string separated by commas. If the incoming array is null, this
1211: * method returns the <code>null</code> string.
1212: *
1213: * @param array Array of strings
1214: * @return Single string containing all the strings from the original array separated by commas, or <code>null</code> if the input was null.
1215: */
1216: public static String arrayOfStringsToString(String[] array) {
1217: return arrayOfStringsToString(array, ", ");
1218: }
1219:
1220: /**
1221: * Returns category information from the path provided to the method where the path provided is
1222: * assumed to be everything after the servlet instance with a user id at the very beginning of the path.
1223: * For example, /david/this/is/the/category
1224: *
1225: * @param pathInfo Path information
1226: * @return Everything after the second "/" character in the path
1227: */
1228: public static String getCategoryFromPath(String pathInfo) {
1229: if (pathInfo == null || "/".equals(pathInfo)) {
1230: return "/";
1231: } else {
1232: int categoryStart = pathInfo.indexOf("/", 1);
1233: if (categoryStart == -1) {
1234: return "/";
1235: } else {
1236: return pathInfo.substring(categoryStart);
1237: }
1238: }
1239: }
1240:
1241: /**
1242: * Returns blog id information from the path provided to the method where the path provided is
1243: * assumed to be everything after the servlet instance with a user id at the very beginning of the path.
1244: * For example, /david/this/is/the/category
1245: *
1246: * @param pathInfo Path information
1247: * @return Everything before the second "/" character in the path
1248: */
1249: public static String getBlogFromPath(String pathInfo) {
1250: if (pathInfo == null || "/".equals(pathInfo)) {
1251: return null;
1252: } else {
1253: int userEnd = pathInfo.indexOf("/", 1);
1254: if (userEnd == -1) {
1255: return pathInfo.substring(1);
1256: } else {
1257: return pathInfo.substring(1, userEnd);
1258: }
1259: }
1260: }
1261:
1262: /**
1263: * Delete a directory (or file) and any sub-directories underneath the directory
1264: *
1265: * @param directoryOrFile Directory or file to be deleted
1266: * @return <code>true</code> if the directory (or file) could be deleted, <code>false</code> otherwise
1267: */
1268: public static boolean deleteDirectory(File directoryOrFile) {
1269: return deleteDirectory(directoryOrFile, true);
1270: }
1271:
1272: /**
1273: * Delete a directory (or file) and any sub-directories underneath the directory
1274: *
1275: * @param directoryOrFile Directory or file to be deleted
1276: * @param removeDirectoryOrFile If the directory of file should be deleted in addition to the sub-directories
1277: * @return <code>true</code> if the directory (or file) could be deleted, <code>false</code> otherwise
1278: */
1279: public static boolean deleteDirectory(File directoryOrFile,
1280: boolean removeDirectoryOrFile) {
1281: if (directoryOrFile.isDirectory()) {
1282: File[] children = directoryOrFile.listFiles();
1283: if (children != null && children.length > 0) {
1284: for (int i = 0; i < children.length; i++) {
1285: boolean success = deleteDirectory(children[i]);
1286: if (!success) {
1287: return false;
1288: }
1289: }
1290: }
1291: }
1292:
1293: return !removeDirectoryOrFile || directoryOrFile.delete();
1294: }
1295:
1296: /**
1297: * Recursively copy a directory from a source to a target
1298: *
1299: * @param sourceDirectory Source directory
1300: * @param targetDirectory Destination directory
1301: * @throws IOException If there is an error copying the files and directories
1302: */
1303: public static void copyDirectory(File sourceDirectory,
1304: File targetDirectory) throws IOException {
1305: File[] sourceFiles = sourceDirectory.listFiles(FILE_FILTER);
1306: File[] sourceDirectories = sourceDirectory
1307: .listFiles(DIRECTORY_FILTER);
1308:
1309: targetDirectory.mkdirs();
1310:
1311: // Copy the files
1312: if (sourceFiles != null && sourceFiles.length > 0) {
1313: for (int i = 0; i < sourceFiles.length; i++) {
1314: File sourceFile = sourceFiles[i];
1315:
1316: FileInputStream fis = new FileInputStream(sourceFile);
1317: FileOutputStream fos = new FileOutputStream(
1318: targetDirectory + File.separator
1319: + sourceFile.getName());
1320: FileChannel fcin = fis.getChannel();
1321: FileChannel fcout = fos.getChannel();
1322:
1323: ByteBuffer buf = ByteBuffer.allocateDirect(8192);
1324: long size = fcin.size();
1325: long n = 0;
1326: while (n < size) {
1327: buf.clear();
1328: if (fcin.read(buf) < 0) {
1329: break;
1330: }
1331: buf.flip();
1332: n += fcout.write(buf);
1333: }
1334:
1335: fcin.close();
1336: fcout.close();
1337: fis.close();
1338: fos.close();
1339: }
1340: }
1341:
1342: // Copy the directories
1343: if (sourceDirectories != null && sourceDirectories.length > 0) {
1344: for (int i = 0; i < sourceDirectories.length; i++) {
1345: File directory = sourceDirectories[i];
1346: File newTargetDirectory = new File(targetDirectory,
1347: directory.getName());
1348:
1349: copyDirectory(directory, newTargetDirectory);
1350: }
1351: }
1352: }
1353:
1354: /**
1355: * Turn an array of strings into a Map where the keys and values are the input strings. If the incoming array is null, this
1356: * method returns an empty map.
1357: *
1358: * @param array Array of strings
1359: * @return Map Map containing all the strings from the original array or an empty map if the incoming array is null.
1360: */
1361: public static Map arrayOfStringsToMap(String[] array) {
1362: if (array == null) {
1363: return new HashMap();
1364: }
1365:
1366: Map result = new HashMap();
1367: for (int i = 0; i < array.length; i++) {
1368: result.put(array[i], array[i]);
1369: }
1370:
1371: return result;
1372: }
1373:
1374: /**
1375: * Add a '/' at the beginning and end of the input string if necessary.
1376: *
1377: * @param input Input string
1378: * @return String with a '/' at the beginning and end of the original string, <code>null</code> if the input was <code>null</code>
1379: */
1380: public static String checkStartingAndEndingSlash(String input) {
1381: if (input == null) {
1382: return null;
1383: }
1384:
1385: if (!input.startsWith("/")) {
1386: input = "/" + input;
1387: }
1388:
1389: if (!input.endsWith("/")) {
1390: input += "/";
1391: }
1392:
1393: return input;
1394: }
1395:
1396: /**
1397: * Checks to see if the string is null or blank (after trimming)
1398: *
1399: * @param input Input string
1400: * @return <code>true</code> if the string is null or blank (after trimming), <code>false</code> otherwise
1401: */
1402: public static boolean checkNullOrBlank(String input) {
1403: return (input == null || "".equals(input.trim()));
1404: }
1405:
1406: /**
1407: * Set various cache control HTTP headers so that the browser does not try and cache the page
1408: *
1409: * @param httpServletResponse Response
1410: */
1411: public static void setNoCacheControlHeaders(
1412: HttpServletResponse httpServletResponse) {
1413: httpServletResponse.setHeader(PRAGMA_HTTP_HEADER,
1414: NO_CACHE_HTTP_HEADER_VALUE);
1415: httpServletResponse.setHeader(CACHE_CONTROL_HTTP_HEADER,
1416: NO_CACHE_HTTP_HEADER_VALUE);
1417: }
1418:
1419: /**
1420: * Check to see if a given map contains a particular key. Returns <code>true</code> if and only if the map and
1421: * key are not null and the map contains the key.
1422: *
1423: * @param map Map to check for given key
1424: * @param key Key to check for in map
1425: * @return Returns <code>true</code> if and only if the map and key are not null and the map contains the key.
1426: */
1427: public static boolean checkMapForKey(Map map, String key) {
1428: return map != null && (key != null && map.containsKey(key));
1429: }
1430:
1431: /**
1432: * Return the number of days between two dates
1433: *
1434: * @param startDate Start date
1435: * @param endDate End date
1436: * @return Number of days between two dates which may be 0 if either of the dates if <code>null</code>
1437: */
1438: public static int daysBetweenDates(Date startDate, Date endDate) {
1439: if (startDate == null || endDate == null) {
1440: return 0;
1441: }
1442:
1443: Calendar calendarStartDate = Calendar.getInstance();
1444: calendarStartDate.setTime(startDate);
1445: int startDay = calendarStartDate.get(Calendar.DAY_OF_YEAR);
1446: int startYear = calendarStartDate.get(Calendar.YEAR);
1447: Calendar calendarEndDate = Calendar.getInstance();
1448: calendarEndDate.setTime(endDate);
1449: int endDay = calendarEndDate.get(Calendar.DAY_OF_YEAR);
1450: int endYear = calendarEndDate.get(Calendar.YEAR);
1451:
1452: return Math.abs((endDay - startDay)
1453: + ((endYear - startYear) * 365));
1454: }
1455:
1456: /**
1457: * Return a filename with the date as a long value before the file extension.
1458: *
1459: * @param filename Filename with extension
1460: * @return Filename as {filename}-{date}.{file extension} or <code>null</code> if there was no file extension
1461: */
1462: public static File getFilenameForDate(String filename) {
1463: String filenameWithoutExtension = getFilename(filename);
1464: String fileExtension = getFileExtension(filename);
1465:
1466: if (fileExtension == null) {
1467: return null;
1468: } else {
1469: return new File(filenameWithoutExtension + "-"
1470: + new Date().getTime() + "." + fileExtension);
1471: }
1472: }
1473:
1474: /**
1475: * Strip line terminator characters from an input string
1476: *
1477: * @param input Input string
1478: * @return Input with line terminator characters stripped or <code>null</code> if the input was <code>null</code>
1479: */
1480: public static String stripLineTerminators(String input) {
1481: return stripLineTerminators(input, "");
1482: }
1483:
1484: /**
1485: * Strip line terminator characters from an input string
1486: *
1487: * @param input Input string
1488: * @param replacement Replacement string
1489: * @return Input with line terminator characters stripped or <code>null</code> if the input was <code>null</code>
1490: */
1491: public static String stripLineTerminators(String input,
1492: String replacement) {
1493: if (input == null) {
1494: return null;
1495: }
1496:
1497: return input.replaceAll("[\n\r\f]", replacement);
1498: }
1499:
1500: /**
1501: * Return the keys of a map as a comma-separated list
1502: *
1503: * @param input {@link Map}
1504: * @return Keys as a comma-separated list or an empty string if the input is <code>null</code> or contains no keys
1505: */
1506: public static String getKeysAsStringList(Map input) {
1507: StringBuffer result = new StringBuffer();
1508: if (input == null || input.size() == 0) {
1509: return result.toString();
1510: }
1511:
1512: Iterator keyIterator = input.keySet().iterator();
1513: int counter = 0;
1514: while (keyIterator.hasNext()) {
1515: Object key = keyIterator.next();
1516: result.append(key);
1517:
1518: if (counter < input.size() - 1) {
1519: result.append(", ");
1520: }
1521:
1522: counter++;
1523: }
1524:
1525: return result.toString();
1526: }
1527:
1528: /**
1529: * Convert a list to a comma-separated string. If values in the list are <code>null</code>, a
1530: * space is printed. If the input is null or there are no items in the list, an empty
1531: * string is returned.
1532: *
1533: * @param values List of values
1534: * @return Comma-separated string
1535: */
1536: public static String listToCSV(List values) {
1537: StringBuffer result = new StringBuffer();
1538:
1539: if (values != null && values.size() > 0) {
1540: for (int i = 0; i < values.size(); i++) {
1541: if (values.get(i) == null) {
1542: result.append(" ");
1543: } else {
1544: result.append(values.get(i));
1545: }
1546:
1547: if (i < values.size() - 1) {
1548: result.append(", ");
1549: }
1550: }
1551: }
1552:
1553: return result.toString();
1554: }
1555:
1556: /**
1557: * Convert a list of values to a {@link Map}. <code>null</code> values are not placed
1558: * in the returned <code>Map</code>.
1559: *
1560: * @param values List of values
1561: * @return {@link Map} where each key and value pair is from the list of values
1562: */
1563: public static Map listToMap(List values) {
1564: Map valueMap = new HashMap();
1565:
1566: if (values != null && values.size() > 0) {
1567: Iterator valueIterator = values.iterator();
1568: Object value;
1569: while (valueIterator.hasNext()) {
1570: value = valueIterator.next();
1571: if (value != null) {
1572: valueMap.put(value, value);
1573: }
1574: }
1575: }
1576:
1577: return valueMap;
1578: }
1579:
1580: /**
1581: * Return a comma-separated list of Strings as a {@link List}; trims space around value
1582: *
1583: * @param valuesAsString Comma-separated values
1584: * @return Comma-separated list of Strings as a {@link List}
1585: */
1586: public static List csvToList(String valuesAsString) {
1587: String[] values = parseOnlyCommaList(valuesAsString);
1588: ArrayList updated = new ArrayList();
1589: for (int i = 0; i < values.length; i++) {
1590: String value = values[i].trim();
1591: updated.add(value);
1592: }
1593:
1594: return updated;
1595: }
1596:
1597: /**
1598: * Construct a blog base URL from the request
1599: *
1600: * @param httpServletRequest Request
1601: * @return URL of the form <code>http://server:port/context_path</code>
1602: */
1603: public static String constructBaseURL(
1604: HttpServletRequest httpServletRequest) {
1605: StringBuffer result = new StringBuffer();
1606:
1607: result.append(httpServletRequest.getScheme()).append("://");
1608: result.append(httpServletRequest.getServerName());
1609: if (httpServletRequest.getServerPort() != 80) {
1610: result.append(":").append(
1611: httpServletRequest.getServerPort());
1612: }
1613: result.append(httpServletRequest.getContextPath());
1614:
1615: return result.toString();
1616: }
1617:
1618: /**
1619: * Construct a blog URL from the request
1620: *
1621: * @param httpServletRequest Request
1622: * @param blogID Blog ID
1623: * @return URL of the form <code>http://server:port/context_path/servlet_path/blog_id/</code>
1624: */
1625: public static String constructBlogURL(
1626: HttpServletRequest httpServletRequest, String blogID) {
1627: StringBuffer result = new StringBuffer(
1628: constructBaseURL(httpServletRequest));
1629:
1630: result.append(httpServletRequest.getServletPath()).append("/")
1631: .append(blogID);
1632:
1633: return result.toString();
1634: }
1635:
1636: /**
1637: * Construct a blog URL from the request
1638: *
1639: * @param httpServletRequest Request
1640: * @param blogID Blog ID
1641: * @param servletPath Servlet path
1642: * @return URL of the form <code>http://server:port/context_path/servlet_path/blog_id/</code>
1643: */
1644: public static String constructBlogURL(
1645: HttpServletRequest httpServletRequest, String blogID,
1646: String servletPath) {
1647: StringBuffer result = new StringBuffer(
1648: constructBaseURL(httpServletRequest));
1649:
1650: result.append("/").append(servletPath).append("/").append(
1651: blogID);
1652:
1653: return result.toString();
1654: }
1655:
1656: /**
1657: * Return a digested string of some content
1658: *
1659: * @param content Content from which to generate a hashed digest
1660: * @return {@link BlojsomUtils#digestString(String)} Digested string
1661: */
1662: public static String getHashableContent(String content) {
1663: String hashable = content;
1664:
1665: if (content.length() > MAX_HASHABLE_LENGTH) {
1666: hashable = hashable.substring(0, MAX_HASHABLE_LENGTH);
1667: }
1668:
1669: return digestString(hashable).toUpperCase();
1670: }
1671:
1672: /**
1673: * Return a filename appropriate for the blog entry content
1674: *
1675: * @param title Blog entry title
1676: * @param content Blog entry content
1677: * @return Filename for the new blog entry
1678: */
1679: public static String getPostSlug(String title, String content) {
1680: String slug;
1681:
1682: if (!checkNullOrBlank(title)) {
1683: slug = title.replaceAll("\\s", "_");
1684: slug = slug.replaceAll("'", "");
1685: slug = slug.replaceAll("\\p{Punct}", "_");
1686: slug = slug.replaceAll("_{2,}", "_");
1687: slug = slug.replaceAll("_", "-");
1688: String backup = slug;
1689: slug = slug.replaceAll("^-{1,}", "");
1690: slug = slug.replaceAll("-{1,}$", "");
1691: if (checkNullOrBlank(slug)) {
1692: slug = backup;
1693: }
1694: } else {
1695: slug = getHashableContent(content);
1696: }
1697:
1698: return slug;
1699: }
1700:
1701: /**
1702: * Create a {@link Locale} object from a string of form <code>language_country_variant</code>
1703: *
1704: * @param locale Locale string of form <code>language_country_variant</code>
1705: * @return {@link Locale} object with language, country, variant settings or {@link java.util.Locale#getDefault()}
1706: * if <code>locale</code> input is <code>null</code> or blank
1707: */
1708: public static Locale getLocaleFromString(String locale) {
1709: if (checkNullOrBlank(locale)) {
1710: return Locale.getDefault();
1711: }
1712:
1713: String language = locale;
1714: String country = "";
1715: String variant = "";
1716:
1717: // Check for language
1718: int index = language.indexOf('_');
1719: if (index >= 0) {
1720: country = language.substring(index + 1);
1721: language = language.substring(0, index);
1722: }
1723:
1724: // Check for country and variant
1725: index = country.indexOf('_');
1726: if (index >= 0) {
1727: variant = country.substring(index + 1);
1728: country = country.substring(0, index);
1729: }
1730:
1731: return new Locale(language, country, variant);
1732: }
1733:
1734: /**
1735: * Return of a list of locale languages supported on this system (JVM)
1736: *
1737: * @param locale {@link Locale} used for sorting
1738: * @return List of locale languages supported on this system (JVM)
1739: */
1740: public static String[] getLanguagesForSystem(Locale locale) {
1741: Locale[] installedLocales = Locale.getAvailableLocales();
1742: ArrayList languageList = new ArrayList(installedLocales.length);
1743: String[] languages;
1744: String language;
1745:
1746: for (int i = 0; i < installedLocales.length; i++) {
1747: Locale installedLocale = installedLocales[i];
1748: language = installedLocale.getLanguage();
1749: if (!languageList.contains(language)
1750: && !checkNullOrBlank(language)) {
1751: languageList.add(language);
1752: }
1753: }
1754:
1755: languages = (String[]) languageList
1756: .toArray(new String[languageList.size()]);
1757: Collator collator = Collator.getInstance(locale);
1758: Arrays.sort(languages, collator);
1759:
1760: return languages;
1761: }
1762:
1763: /**
1764: * Return of a list of locale countries supported on this system (JVM)
1765: *
1766: * @param locale {@link Locale} used for sorting
1767: * @return Return of a list of locale countries supported on this system (JVM)
1768: */
1769: public static String[] getCountriesForSystem(Locale locale) {
1770: Locale[] installedLocales = Locale.getAvailableLocales();
1771: ArrayList countryList = new ArrayList(installedLocales.length);
1772: String[] countries;
1773: String country;
1774:
1775: for (int i = 0; i < installedLocales.length; i++) {
1776: Locale installedLocale = installedLocales[i];
1777: country = installedLocale.getCountry();
1778: if (!countryList.contains(country)
1779: && !checkNullOrBlank(country)) {
1780: countryList.add(country);
1781: }
1782: }
1783:
1784: countries = (String[]) countryList
1785: .toArray(new String[countryList.size()]);
1786: Collator collator = Collator.getInstance(locale);
1787: Arrays.sort(countries, collator);
1788:
1789: return countries;
1790: }
1791:
1792: /**
1793: * Return of a list of time zone IDs supported on this system (JVM)
1794: *
1795: * @param locale {@link Locale} used for sorting
1796: * @return Return of a list of time zone IDs supported on this system (JVM)
1797: */
1798: public static String[] getTimeZonesForSystem(Locale locale) {
1799: String[] timezones = TimeZone.getAvailableIDs();
1800:
1801: Collator collator = Collator.getInstance(locale);
1802: Arrays.sort(timezones, collator);
1803:
1804: return timezones;
1805: }
1806:
1807: /**
1808: * List the files in a sub-directory of a given directory and strip the parent directory from the path
1809: * of the files added to the list.
1810: *
1811: * @param directory Sub-directory to start looking for files
1812: * @param parentDirectory Parent directory to strip
1813: * @param files List of files to add to
1814: */
1815: public static void listFilesInSubdirectories(File directory,
1816: String parentDirectory, List files) {
1817: if (directory.isDirectory()) {
1818: String[] children = directory.list();
1819: for (int i = 0; i < children.length; i++) {
1820: listFilesInSubdirectories(new File(directory,
1821: children[i]), parentDirectory, files);
1822: }
1823: } else {
1824: if (directory.getPath().startsWith(parentDirectory)) {
1825: files.add(new File(directory.getPath().substring(
1826: parentDirectory.length() + 1)));
1827: }
1828: }
1829: }
1830:
1831: /**
1832: * List the sub-directories in a sub-directory of a given directory and strip the parent directory from the path
1833: * of the directories added to the list.
1834: *
1835: * @param directory Sub-directory to start looking for files
1836: * @param parentDirectory Parent directory to strip
1837: * @param directories List of directories to add to
1838: */
1839: public static void listDirectoriesInSubdirectories(File directory,
1840: String parentDirectory, List directories) {
1841: if (directory.isDirectory()) {
1842: String[] children = directory.list();
1843: for (int i = 0; i < children.length; i++) {
1844: listDirectoriesInSubdirectories(new File(directory,
1845: children[i]), parentDirectory, directories);
1846: }
1847:
1848: if (directory.getPath().startsWith(parentDirectory)) {
1849: directories.add(new File(directory.getPath().substring(
1850: parentDirectory.length())));
1851: }
1852: }
1853: }
1854:
1855: /**
1856: * Strip all HTML from a given piece of text
1857: *
1858: * @param text Text
1859: * @return text stripped of HTML between < and > tags or <code>null</code> if input was null or blank if input was blank
1860: */
1861: public static String stripHTML(String text) {
1862: if (checkNullOrBlank(text)) {
1863: return text;
1864: }
1865:
1866: Matcher m = STRIP_HTML_PATTERN.matcher(text);
1867:
1868: return m.replaceAll("");
1869: }
1870:
1871: /**
1872: * Convert a <code>String[]</code> to a <code>List</code>
1873: *
1874: * @param input <code>String[]</code>
1875: * @return <code>List</code> from string array
1876: */
1877: public static List arrayToList(String[] input) {
1878: if (input == null || input.length == 0) {
1879: return new ArrayList();
1880: } else {
1881: ArrayList value = new ArrayList(input.length);
1882:
1883: for (int i = 0; i < input.length; i++) {
1884: String s = input[i];
1885: value.add(s);
1886: }
1887:
1888: return value;
1889: }
1890: }
1891:
1892: /**
1893: * Remove <code>null</code> values from a given list
1894: *
1895: * @param input List
1896: * @return List with <code>null</code> values removed
1897: */
1898: public static List removeNullValues(List input) {
1899: if (input == null) {
1900: return new ArrayList();
1901: } else {
1902: ArrayList sanitizedList = new ArrayList(input.size());
1903:
1904: for (int i = 0; i < input.size(); i++) {
1905: if (input.get(i) != null) {
1906: sanitizedList.add(input.get(i));
1907: }
1908: }
1909:
1910: return sanitizedList;
1911: }
1912: }
1913:
1914: /**
1915: * Add preceeding and trailing slashes to an input string. If input is <code>null</code> a "/" is returned.
1916: *
1917: * @param input Input
1918: * @return Input with preceeding and trailing slashes added
1919: */
1920: public static String addSlashes(String input) {
1921: if (input == null) {
1922: return "/";
1923: }
1924:
1925: if (!input.startsWith("/")) {
1926: input = "/" + input;
1927: }
1928:
1929: if (!input.endsWith("/")) {
1930: input += "/";
1931: }
1932:
1933: return input;
1934: }
1935:
1936: /**
1937: * Add a trailing slash to the input
1938: *
1939: * @param input Input
1940: * @return Input with trailing slash added
1941: */
1942: public static String addTrailingSlash(String input) {
1943: if (input == null) {
1944: return "/";
1945: }
1946:
1947: if (!input.endsWith("/")) {
1948: input += "/";
1949: }
1950:
1951: return input;
1952: }
1953:
1954: /**
1955: * Check to see if the blog base URL or blog URL are present. If not, construct them dynamically by calling
1956: * {@link #constructBaseURL(javax.servlet.http.HttpServletRequest)} and {@link #constructBlogURL(javax.servlet.http.HttpServletRequest, String)}.
1957: *
1958: * @param httpServletRequest Request
1959: * @param blog {@link org.blojsom.blog.Blog}
1960: * @param blogID Blog ID
1961: */
1962: public static void resolveDynamicBaseAndBlogURL(
1963: HttpServletRequest httpServletRequest, Blog blog,
1964: String blogID) {
1965: blog.setBlogBaseURL(constructBaseURL(httpServletRequest));
1966: blog.setBlogURL(constructBlogURL(httpServletRequest, blogID));
1967:
1968: blog.setBlogBaseAdminURL(constructBaseURL(httpServletRequest));
1969: blog.setBlogAdminURL(constructBlogURL(httpServletRequest,
1970: blogID));
1971: }
1972:
1973: /**
1974: * Check to see if the blog base URL or blog URL are present. If not, construct them dynamically by calling
1975: * {@link #constructBaseURL(javax.servlet.http.HttpServletRequest)} and {@link #constructBlogURL(javax.servlet.http.HttpServletRequest, String)}.
1976: *
1977: * @param httpServletRequest Request
1978: * @param blog {@link org.blojsom.blog.Blog}
1979: * @param blogID Blog ID
1980: * @param servletPath Custom servlet path
1981: */
1982: public static void resolveDynamicBaseAndBlogURL(
1983: HttpServletRequest httpServletRequest, Blog blog,
1984: String blogID, String servletPath) {
1985: blog.setBlogBaseURL(constructBaseURL(httpServletRequest));
1986: blog.setBlogURL(constructBlogURL(httpServletRequest, blogID,
1987: servletPath));
1988:
1989: blog.setBlogBaseAdminURL(constructBaseURL(httpServletRequest));
1990: blog.setBlogAdminURL(constructBlogURL(httpServletRequest,
1991: blogID, servletPath));
1992: }
1993:
1994: /**
1995: * Return a {@link List} as a string
1996: *
1997: * @param values {@link List} of values
1998: * @param separator Separator in-between values
1999: * @return {@link List} as a string where each item is separated by the <code>separator</code>
2000: */
2001: public static String listToString(List values, String separator) {
2002: StringBuffer valuesAsString = new StringBuffer();
2003:
2004: if (values != null && values.size() > 0) {
2005: for (int i = 0; i < values.size(); i++) {
2006: String value = (String) values.get(i);
2007: valuesAsString.append(value);
2008: if (i < values.size() - 1) {
2009: valuesAsString.append(separator);
2010: }
2011: }
2012: }
2013:
2014: return valuesAsString.toString();
2015: }
2016:
2017: public static Comparator RESPONSE_COMPARATOR = new Comparator() {
2018: public int compare(Object object, Object object1) {
2019: if (object instanceof Response
2020: && object1 instanceof Response) {
2021: Response obj = (Response) object;
2022: Response obj1 = (Response) object1;
2023:
2024: if (obj.getDate().before(obj1.getDate())) {
2025: return -1;
2026: } else if (obj.getDate().after(obj1.getDate())) {
2027: return 1;
2028: }
2029: }
2030:
2031: return 0;
2032: }
2033: };
2034:
2035: /**
2036: * Find the first date of a year
2037: *
2038: * @param locale Locale
2039: * @param year Year
2040: * @return First date of the requested year
2041: */
2042: public static Date getFirstDateOfYear(Locale locale, int year) {
2043: Calendar calendar = Calendar.getInstance(locale);
2044:
2045: calendar.set(Calendar.YEAR, year);
2046: calendar.set(Calendar.MONTH, calendar
2047: .getActualMinimum(Calendar.MONTH));
2048: calendar.set(Calendar.DAY_OF_MONTH, calendar
2049: .getActualMinimum(Calendar.DAY_OF_MONTH));
2050: calendar.set(Calendar.HOUR_OF_DAY, calendar
2051: .getActualMinimum(Calendar.HOUR_OF_DAY));
2052: calendar.set(Calendar.MINUTE, calendar
2053: .getActualMinimum(Calendar.MINUTE));
2054: calendar.set(Calendar.SECOND, calendar
2055: .getActualMinimum(Calendar.SECOND));
2056: calendar.set(Calendar.MILLISECOND, calendar
2057: .getActualMinimum(Calendar.MILLISECOND));
2058:
2059: return calendar.getTime();
2060: }
2061:
2062: /**
2063: * Find the last date of a year
2064: *
2065: * @param locale Locale
2066: * @param year Year
2067: * @return Last date of the requested year
2068: */
2069: public static Date getLastDateOfYear(Locale locale, int year) {
2070: Calendar calendar = Calendar.getInstance(locale);
2071:
2072: calendar.set(Calendar.YEAR, year);
2073: calendar.set(Calendar.MONTH, calendar
2074: .getActualMaximum(Calendar.MONTH));
2075: calendar.set(Calendar.DAY_OF_MONTH, calendar
2076: .getActualMaximum(Calendar.DAY_OF_MONTH));
2077: calendar.set(Calendar.HOUR_OF_DAY, calendar
2078: .getActualMaximum(Calendar.HOUR_OF_DAY));
2079: calendar.set(Calendar.MINUTE, calendar
2080: .getActualMaximum(Calendar.MINUTE));
2081: calendar.set(Calendar.SECOND, calendar
2082: .getActualMaximum(Calendar.SECOND));
2083: calendar.set(Calendar.MILLISECOND, calendar
2084: .getActualMaximum(Calendar.MILLISECOND));
2085:
2086: return calendar.getTime();
2087: }
2088:
2089: /**
2090: * Find the first date of a year/month
2091: *
2092: * @param locale Locale
2093: * @param year Year
2094: * @param month Month
2095: * @return FIrst date of the requested year/month
2096: */
2097: public static Date getFirstDateOfYearMonth(Locale locale, int year,
2098: int month) {
2099: Calendar calendar = Calendar.getInstance(locale);
2100:
2101: calendar.set(Calendar.YEAR, year);
2102: calendar.set(Calendar.MONTH, month);
2103: calendar.set(Calendar.DAY_OF_MONTH, calendar
2104: .getActualMinimum(Calendar.DAY_OF_MONTH));
2105: calendar.set(Calendar.HOUR_OF_DAY, calendar
2106: .getActualMinimum(Calendar.HOUR_OF_DAY));
2107: calendar.set(Calendar.MINUTE, calendar
2108: .getActualMinimum(Calendar.MINUTE));
2109: calendar.set(Calendar.SECOND, calendar
2110: .getActualMinimum(Calendar.SECOND));
2111: calendar.set(Calendar.MILLISECOND, calendar
2112: .getActualMinimum(Calendar.MILLISECOND));
2113:
2114: return calendar.getTime();
2115: }
2116:
2117: /**
2118: * Find the last date of a year/month
2119: *
2120: * @param locale Locale
2121: * @param year Year
2122: * @param month Month
2123: * @return Last date of the requested year/month
2124: */
2125: public static Date getLastDateOfYearMonth(Locale locale, int year,
2126: int month) {
2127: Calendar calendar = Calendar.getInstance(locale);
2128:
2129: calendar.set(Calendar.YEAR, year);
2130: calendar.set(Calendar.MONTH, month);
2131: calendar.set(Calendar.DAY_OF_MONTH, calendar
2132: .getActualMaximum(Calendar.DAY_OF_MONTH));
2133: calendar.set(Calendar.HOUR_OF_DAY, calendar
2134: .getActualMaximum(Calendar.HOUR_OF_DAY));
2135: calendar.set(Calendar.MINUTE, calendar
2136: .getActualMaximum(Calendar.MINUTE));
2137: calendar.set(Calendar.SECOND, calendar
2138: .getActualMaximum(Calendar.SECOND));
2139: calendar.set(Calendar.MILLISECOND, calendar
2140: .getActualMaximum(Calendar.MILLISECOND));
2141:
2142: return calendar.getTime();
2143: }
2144:
2145: /**
2146: * Get the first date of a year/month/day
2147: *
2148: * @param locale Locale
2149: * @param year Year
2150: * @param month Month
2151: * @param day Day
2152: * @return First date of the requested year/month/day
2153: */
2154: public static Date getFirstDateOfYearMonthDay(Locale locale,
2155: int year, int month, int day) {
2156: Calendar calendar = Calendar.getInstance(locale);
2157:
2158: calendar.set(Calendar.YEAR, year);
2159: calendar.set(Calendar.MONTH, month);
2160: if (day < calendar.getActualMinimum(Calendar.DAY_OF_MONTH)) {
2161: calendar.set(Calendar.DAY_OF_MONTH, calendar
2162: .getActualMinimum(Calendar.DAY_OF_MONTH));
2163: } else {
2164: calendar.set(Calendar.DAY_OF_MONTH, day);
2165: }
2166: calendar.set(Calendar.HOUR_OF_DAY, calendar
2167: .getActualMinimum(Calendar.HOUR_OF_DAY));
2168: calendar.set(Calendar.MINUTE, calendar
2169: .getActualMinimum(Calendar.MINUTE));
2170: calendar.set(Calendar.SECOND, calendar
2171: .getActualMinimum(Calendar.SECOND));
2172: calendar.set(Calendar.MILLISECOND, calendar
2173: .getActualMinimum(Calendar.MILLISECOND));
2174:
2175: return calendar.getTime();
2176: }
2177:
2178: /**
2179: * Get the last date of a year/month/day
2180: *
2181: * @param locale Locale
2182: * @param year Year
2183: * @param month Month
2184: * @param day Day
2185: * @return Last date of the requested year/month/day
2186: */
2187: public static Date getLastDateOfYearMonthDay(Locale locale,
2188: int year, int month, int day) {
2189: Calendar calendar = Calendar.getInstance(locale);
2190:
2191: calendar.set(Calendar.YEAR, year);
2192: calendar.set(Calendar.MONTH, month);
2193: if (day > calendar.getActualMaximum(Calendar.DAY_OF_MONTH)) {
2194: calendar.set(Calendar.DAY_OF_MONTH, calendar
2195: .getActualMaximum(Calendar.DAY_OF_MONTH));
2196: } else {
2197: calendar.set(Calendar.DAY_OF_MONTH, day);
2198: }
2199: calendar.set(Calendar.HOUR_OF_DAY, calendar
2200: .getActualMaximum(Calendar.HOUR_OF_DAY));
2201: calendar.set(Calendar.MINUTE, calendar
2202: .getActualMaximum(Calendar.MINUTE));
2203: calendar.set(Calendar.SECOND, calendar
2204: .getActualMaximum(Calendar.SECOND));
2205: calendar.set(Calendar.MILLISECOND, calendar
2206: .getActualMaximum(Calendar.MILLISECOND));
2207:
2208: return calendar.getTime();
2209: }
2210: }
|