001: /**
002: * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the latest version of the GNU Lesser General
006: * Public License as published by the Free Software Foundation;
007: *
008: * This program is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
011: * GNU Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public License
014: * along with this program (LICENSE.txt); if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
016: */package org.jamwiki.utils;
017:
018: import java.io.File;
019: import java.io.FileNotFoundException;
020: import java.net.URL;
021: import java.net.URLDecoder;
022: import java.net.URLEncoder;
023: import java.text.MessageFormat;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Locale;
027: import java.util.Map;
028: import java.util.ResourceBundle;
029: import java.util.regex.Matcher;
030: import java.util.regex.Pattern;
031: import org.apache.commons.io.FileUtils;
032: import org.apache.commons.lang.ObjectUtils;
033: import org.apache.commons.lang.StringUtils;
034:
035: /**
036: * This class provides a variety of basic utility methods that are not
037: * dependent on any other classes within the org.jamwiki package structure.
038: */
039: public class Utilities {
040:
041: private static final WikiLogger logger = WikiLogger
042: .getLogger(Utilities.class.getName());
043:
044: private static Pattern VALID_IPV4_PATTERN = null;
045: private static Pattern VALID_IPV6_PATTERN = null;
046: private static final String ipv4Pattern = "(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])";
047: private static final String ipv6Pattern = "([0-9a-f]{1,4}:){7}([0-9a-f]){1,4}";
048:
049: static {
050: try {
051: VALID_IPV4_PATTERN = Pattern.compile(ipv4Pattern,
052: Pattern.CASE_INSENSITIVE);
053: VALID_IPV6_PATTERN = Pattern.compile(ipv6Pattern,
054: Pattern.CASE_INSENSITIVE);
055: } catch (Exception e) {
056: logger.severe("Unable to compile pattern", e);
057: }
058: }
059:
060: /**
061: *
062: */
063: private Utilities() {
064: }
065:
066: /**
067: * Convert a string value from one encoding to another.
068: *
069: * @param text The string that is to be converted.
070: * @param fromEncoding The encoding that the string is currently encoded in.
071: * @param toEncoding The encoding that the string is to be encoded to.
072: * @return The encoded string.
073: */
074: public static String convertEncoding(String text,
075: String fromEncoding, String toEncoding) {
076: if (StringUtils.isBlank(text)) {
077: return text;
078: }
079: if (StringUtils.isBlank(fromEncoding)) {
080: logger
081: .warning("No character encoding specified to convert from, using UTF-8");
082: fromEncoding = "UTF-8";
083: }
084: if (StringUtils.isBlank(toEncoding)) {
085: logger
086: .warning("No character encoding specified to convert to, using UTF-8");
087: toEncoding = "UTF-8";
088: }
089: try {
090: text = new String(text.getBytes(fromEncoding), toEncoding);
091: } catch (Exception e) {
092: // bad encoding
093: logger.warning("Unable to convert value " + text + " from "
094: + fromEncoding + " to " + toEncoding, e);
095: }
096: return text;
097: }
098:
099: /**
100: * Decode a value that has been retrieved from a servlet request. This
101: * method will replace any underscores with spaces.
102: *
103: * @param url The encoded value that is to be decoded.
104: * @param decodeUnderlines Set to <code>true</code> if underlines should
105: * be automatically converted to spaces.
106: * @return A decoded value.
107: */
108: public static String decodeFromRequest(String url,
109: boolean decodeUnderlines) {
110: return (decodeUnderlines) ? StringUtils.replace(url, "_", " ")
111: : url;
112: }
113:
114: /**
115: * Decode a value that has been retrieved directly from a URL or file
116: * name. This method will URL decode the value and then replace any
117: * underscores with spaces. Note that this method SHOULD NOT be called
118: * for values retrieved using request.getParameter(), but only values
119: * taken directly from a URL.
120: *
121: * @param url The encoded value that is to be decoded.
122: * @param decodeUnderlines Set to <code>true</code> if underlines should
123: * be automatically converted to spaces.
124: * @return A decoded value.
125: */
126: public static String decodeFromURL(String url,
127: boolean decodeUnderlines) {
128: String result = url;
129: try {
130: result = URLDecoder.decode(result, "UTF-8");
131: } catch (Exception e) {
132: logger.info("Failure while decoding url " + url
133: + " with charset UTF-8");
134: }
135: return Utilities.decodeFromRequest(result, decodeUnderlines);
136: }
137:
138: /**
139: * Convert a topic name or other value into a value suitable for use as a
140: * file name. This method replaces spaces with underscores, and then URL
141: * encodes the value.
142: *
143: * @param name The value that is to be encoded for use as a file name.
144: * @return The encoded value.
145: */
146: public static String encodeForFilename(String name) {
147: // replace spaces with underscores
148: String result = StringUtils.replace(name, " ", "_");
149: // URL encode the rest of the name
150: try {
151: result = URLEncoder.encode(result, "UTF-8");
152: } catch (Exception e) {
153: logger.warning("Failure while encoding " + name
154: + " with charset UTF-8");
155: }
156: return result;
157: }
158:
159: /**
160: * Encode a topic name for use in a URL. This method will replace spaces
161: * with underscores and URL encode the value, but it will not URL encode
162: * colons.
163: *
164: * @param url The topic name to be encoded for use in a URL.
165: * @return The encoded topic name value.
166: */
167: public static String encodeForURL(String url) {
168: String result = Utilities.encodeForFilename(url);
169: // un-encode colons
170: result = StringUtils.replace(result, "%3A", ":");
171: // un-encode forward slashes
172: result = StringUtils.replace(result, "%2F", "/");
173: return result;
174: }
175:
176: /**
177: * Returns any trailing period, comma, semicolon, or colon characters
178: * from the given string. This method is useful when parsing raw HTML
179: * links, in which case trailing punctuation must be removed.
180: *
181: * @param text The text from which trailing punctuation should be returned.
182: * @return Any trailing punctuation from the given text, or an empty string
183: * otherwise.
184: */
185: public static String extractTrailingPunctuation(String text) {
186: StringBuffer buffer = new StringBuffer();
187: for (int i = text.length() - 1; i >= 0; i--) {
188: char c = text.charAt(i);
189: if (c == '.' || c == ';' || c == ',' || c == ':'
190: || c == ')' || c == '(' || c == ']' || c == '[') {
191: buffer.append(c);
192: } else {
193: break;
194: }
195: }
196: if (buffer.length() == 0) {
197: return "";
198: }
199: buffer = buffer.reverse();
200: return buffer.toString();
201: }
202:
203: /**
204: * Given a message key and locale return a locale-specific message.
205: *
206: * @param key The message key that corresponds to the formatted message
207: * being retrieved.
208: * @param locale The locale for the message that is to be retrieved.
209: * @return A formatted message string that is specific to the locale.
210: */
211: public static String formatMessage(String key, Locale locale) {
212: ResourceBundle messages = ResourceBundle.getBundle(
213: "ApplicationResources", locale);
214: return messages.getString(key);
215: }
216:
217: /**
218: * Given a message key, locale, and formatting parameters, return a
219: * locale-specific message.
220: *
221: * @param key The message key that corresponds to the formatted message
222: * being retrieved.
223: * @param locale The locale for the message that is to be retrieved.
224: * @param params An array of formatting parameters to use in the message
225: * being returned.
226: * @return A formatted message string that is specific to the locale.
227: */
228: public static String formatMessage(String key, Locale locale,
229: Object[] params) {
230: MessageFormat formatter = new MessageFormat("");
231: formatter.setLocale(locale);
232: String message = Utilities.formatMessage(key, locale);
233: formatter.applyPattern(message);
234: return formatter.format(params);
235: }
236:
237: /**
238: * Return the current ClassLoader. First try to get the current thread's
239: * ClassLoader, and if that fails return the ClassLoader that loaded this
240: * class instance.
241: *
242: * @return An instance of the current ClassLoader.
243: */
244: private static ClassLoader getClassLoader() {
245: ClassLoader loader = null;
246: try {
247: loader = Thread.currentThread().getContextClassLoader();
248: } catch (Exception e) {
249: logger
250: .fine("Unable to retrieve thread class loader, trying default");
251: }
252: if (loader == null) {
253: loader = Utilities.class.getClassLoader();
254: }
255: return loader;
256: }
257:
258: /**
259: * Given a file name for a file that is located somewhere in the application
260: * classpath, return a File object representing the file.
261: *
262: * @param filename The name of the file (relative to the classpath) that is
263: * to be retrieved.
264: * @return A file object representing the requested filename
265: * @throws Exception Thrown if the classloader can not be found or if
266: * the file can not be found in the classpath.
267: */
268: public static File getClassLoaderFile(String filename)
269: throws Exception {
270: // note that this method is used when initializing logging, so it must
271: // not attempt to log anything.
272: File file = null;
273: ClassLoader loader = Utilities.getClassLoader();
274: URL url = loader.getResource(filename);
275: if (url == null) {
276: url = ClassLoader.getSystemResource(filename);
277: }
278: if (url == null) {
279: throw new Exception("Unable to find " + filename);
280: }
281: file = FileUtils.toFile(url);
282: if (file == null || !file.exists()) {
283: throw new Exception(
284: "Found invalid root class loader for file "
285: + filename);
286: }
287: return file;
288: }
289:
290: /**
291: * Attempt to get the class loader root directory. This method works
292: * by searching for a file that MUST exist in the class loader root
293: * and then returning its parent directory.
294: *
295: * @return Returns a file indicating the directory of the class loader.
296: * @throws Exception Thrown if the class loader can not be found.
297: */
298: public static File getClassLoaderRoot() throws Exception {
299: // The file hard-coded here MUST be in the class loader directory.
300: File file = Utilities
301: .getClassLoaderFile("ApplicationResources.properties");
302: if (!file.exists()) {
303: throw new Exception("Unable to find class loader root");
304: }
305: return file.getParentFile();
306: }
307:
308: /**
309: * Retrieve the webapp root.
310: *
311: * @return The default webapp root directory.
312: */
313: // FIXME - there HAS to be a utility method available in Spring or some other
314: // common library that offers this functionality.
315: public static File getWebappRoot() throws Exception {
316: // webapp root is two levels above /WEB-INF/classes/
317: return Utilities.getClassLoaderRoot().getParentFile()
318: .getParentFile();
319: }
320:
321: /**
322: * Utility method for determining common elements in two Map objects.
323: */
324: public static Map intersect(Map map1, Map map2) {
325: if (map1 == null || map2 == null) {
326: throw new IllegalArgumentException(
327: "Utilities.intersection() requires non-null arguments");
328: }
329: Map result = new HashMap();
330: Iterator keys = map1.keySet().iterator();
331: while (keys.hasNext()) {
332: Object key = keys.next();
333: if (ObjectUtils.equals(map1.get(key), map2.get(key))) {
334: result.put(key, map1.get(key));
335: }
336: }
337: return result;
338: }
339:
340: /**
341: * Determine if the given string is a valid IPv4 or IPv6 address. This method
342: * uses pattern matching to see if the given string could be a valid IP address.
343: *
344: * @param ipAddress A string that is to be examined to verify whether or not
345: * it could be a valid IP address.
346: * @return <code>true</code> if the string is a value that is a valid IP address,
347: * <code>false</code> otherwise.
348: */
349: public static boolean isIpAddress(String ipAddress) {
350: if (StringUtils.isBlank(ipAddress)) {
351: return false;
352: }
353: Matcher m1 = Utilities.VALID_IPV4_PATTERN.matcher(ipAddress);
354: if (m1.matches()) {
355: return true;
356: }
357: Matcher m2 = Utilities.VALID_IPV6_PATTERN.matcher(ipAddress);
358: return m2.matches();
359: }
360:
361: /**
362: * Utility method for reading a file from a classpath directory and returning
363: * its contents as a String.
364: *
365: * @param filename The name of the file to be read, either as an absolute file
366: * path or relative to the classpath.
367: * @return A string representation of the file contents.
368: * @throws Exception Thrown if the file cannot be found or if an I/O exception
369: * occurs.
370: */
371: public static String readFile(String filename) throws Exception {
372: File file = new File(filename);
373: if (file.exists()) {
374: // file passed in as full path
375: return FileUtils.readFileToString(file, "UTF-8");
376: }
377: // look for file in resource directories
378: ClassLoader loader = Utilities.getClassLoader();
379: URL url = loader.getResource(filename);
380: file = FileUtils.toFile(url);
381: if (file == null || !file.exists()) {
382: throw new FileNotFoundException("File " + filename
383: + " is not available for reading");
384: }
385: return FileUtils.readFileToString(file, "UTF-8");
386: }
387:
388: /**
389: * Strip all HTML tags from a string. For example, "A <b>bold</b> word" will be
390: * returned as "A bold word". This method treats an tags that are between brackets
391: * as HTML, whether it is valid HTML or not.
392: *
393: * @param value The value that will have HTML stripped from it.
394: * @return The value submitted to this method with all HTML tags removed from it.
395: */
396: public static String stripMarkup(String value) {
397: return value.replaceAll("<[^>]+>", "");
398: }
399: }
|