001: /*
002: Very Quick Wiki - WikiWikiWeb clone
003: Copyright (C) 2001-2002 Gareth Cronin
004:
005: This program is free software; you can redistribute it and/or modify
006: it under the terms of the latest version of the GNU Lesser General
007: Public License as published by the Free Software Foundation;
008:
009: This program is distributed in the hope that it will be useful,
010: but WITHOUT ANY WARRANTY; without even the implied warranty of
011: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: GNU Lesser General Public License for more details.
013:
014: You should have received a copy of the GNU Lesser General Public License
015: along with this program (gpl.txt); if not, write to the Free Software
016: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
017: */
018: package vqwiki.utils;
019:
020: import org.apache.log4j.Logger;
021: import org.w3c.dom.Document;
022: import org.xml.sax.SAXException;
023: import vqwiki.Environment;
024: import vqwiki.WikiBase;
025: import vqwiki.WikiException;
026: import vqwiki.file.FileHandler;
027:
028: import javax.servlet.http.Cookie;
029: import javax.servlet.http.HttpServletRequest;
030: import javax.servlet.http.HttpSession;
031: import javax.xml.parsers.DocumentBuilder;
032: import javax.xml.parsers.DocumentBuilderFactory;
033: import javax.xml.parsers.ParserConfigurationException;
034: import java.io.*;
035: import java.text.DateFormat;
036: import java.util.*;
037: import java.util.zip.ZipEntry;
038: import java.util.zip.ZipFile;
039: import java.net.URL;
040: import java.net.MalformedURLException;
041:
042: public class Utilities {
043:
044: private static final Logger logger = Logger
045: .getLogger(Utilities.class);
046: private static final int STATE_NO_ENTITY = 0;
047: private static final int STATE_AMPERSAND = 1;
048: private static final int STATE_AMPERSAND_HASH = 2;
049:
050: /**
051: * Returns true if the given collection of strings contains the given string where the case
052: * of either is ignored
053: * @param collection collection of {@link String}s
054: * @param string string to find
055: * @return true if the string is in the collection with no regard to case
056: */
057: public static boolean containsStringIgnoreCase(
058: Collection collection, String string) {
059: for (Iterator iterator = collection.iterator(); iterator
060: .hasNext();) {
061: String s = (String) iterator.next();
062: if (s.equalsIgnoreCase(string)) {
063: return true;
064: }
065: }
066: return false;
067: }
068:
069: /**
070: *
071: */
072: public static Cookie createUsernameCookie(String username) {
073: Cookie c = new Cookie("username", username);
074: c.setMaxAge(Environment.getInstance().getIntSetting(
075: Environment.PROPERTY_COOKIE_EXPIRE));
076: return c;
077: }
078:
079: /**
080: * Converts HTML integer unicode entities into characters
081: * @param text
082: * @return
083: * @throws java.lang.Exception
084: */
085: public static String convertEntities(String text) throws Exception {
086: StringReader in = new StringReader(text);
087: StringBuffer buffer = new StringBuffer();
088: StringBuffer current = new StringBuffer();
089: int nextByte;
090: int entityMode = STATE_NO_ENTITY;
091: while (-1 != (nextByte = in.read())) {
092: char nextChar = (char) nextByte;
093: if (entityMode == STATE_NO_ENTITY) {
094: if (nextChar != '&') {
095: buffer.append(nextChar);
096: } else {
097: entityMode = STATE_AMPERSAND;
098: current.append(nextChar);
099: }
100: } else if (entityMode == STATE_AMPERSAND) {
101: if (nextChar != '#') {
102: buffer.append(current);
103: buffer.append(nextChar);
104: current = new StringBuffer();
105: entityMode = STATE_NO_ENTITY;
106: } else {
107: entityMode = STATE_AMPERSAND_HASH;
108: current.append(nextChar);
109: }
110: } else {
111: if (nextChar == ';') {
112: String stringValue = current.substring(2, current
113: .length());
114: logger.debug("entity found: " + stringValue);
115: buffer.append((char) Integer.parseInt(stringValue));
116: current = new StringBuffer();
117: entityMode = STATE_NO_ENTITY;
118: } else if (Character.isDigit(nextChar)) {
119: current.append(nextChar);
120: } else {
121: buffer.append(current);
122: current = new StringBuffer();
123: entityMode = STATE_NO_ENTITY;
124: }
125: }
126: }
127: return buffer.toString();
128: }
129:
130: /**
131: * Localised
132: */
133: public static String formatDate(Date date) {
134: return DateFormat.getDateInstance().format(date);
135: }
136:
137: /**
138: *
139: */
140: public static String formatDateTime(Date date) {
141: return DateFormat.getDateTimeInstance().format(date);
142: }
143:
144: /**
145: * Convert the filename-friendly date format back to a Java date
146: */
147: public static Date convertFileFriendlyDate(String filename) {
148: Calendar cal = Calendar.getInstance();
149: int position = filename.lastIndexOf(FileHandler.EXT)
150: + FileHandler.EXT.length();
151: if (position != -1) {
152: // plus one to get rid of the point before the date.
153: filename = filename.substring(position + 1);
154: } else {
155: logger.error("Didn't found Extension " + FileHandler.EXT
156: + " in " + filename);
157: return null;
158: }
159: StringTokenizer tokens = new StringTokenizer(filename, ".");
160: cal.set(Integer.parseInt(tokens.nextToken()), Integer
161: .parseInt(tokens.nextToken()) - 1, Integer
162: .parseInt(tokens.nextToken()), Integer.parseInt(tokens
163: .nextToken()), Integer.parseInt(tokens.nextToken()),
164: Integer.parseInt(tokens.nextToken()));
165: return cal.getTime();
166: }
167:
168: /**
169: * Convert Java date to a file-friendly date
170: */
171: public static String fileFriendlyDate(Date date) {
172: Calendar cal = Calendar.getInstance();
173: cal.setTime(date);
174: return String.valueOf(cal.get(Calendar.YEAR)) + "."
175: + padTensWithZero(cal.get(Calendar.MONTH) + 1) + "."
176: + padTensWithZero(cal.get(Calendar.DATE)) + "."
177: + padTensWithZero(cal.get(Calendar.HOUR_OF_DAY)) + "."
178: + padTensWithZero(cal.get(Calendar.MINUTE)) + "."
179: + padTensWithZero(cal.get(Calendar.SECOND));
180: }
181:
182: /**
183: *
184: */
185: protected static String padTensWithZero(int n) {
186: if (n < 10) {
187: return "0" + n;
188: } else {
189: return String.valueOf(n);
190: }
191: }
192:
193: /**
194: * Convert 3 consecutive spaces to a tab character
195: */
196: public static String convertTabs(String text) {
197: boolean preformatted = false;
198: String converted = "";
199: int spaces = 0;
200: int ats = 0;
201: int linefeeds = 0;
202: for (int i = 0; i < text.length(); i++) {
203: if (text.charAt(i) == ' ') {
204: spaces++;
205: } else if (text.charAt(i) == '@') {
206: ats++;
207: } else if (text.charAt(i) == '\n') {
208: linefeeds++;
209: } else if (text.charAt(i) != '\r') {
210: linefeeds = 0;
211: spaces = 0;
212: ats = 0;
213: } else {
214: spaces = 0;
215: ats = 0;
216: }
217: converted += text.charAt(i);
218: if (ats == 4) {
219: preformatted = true;
220: ats = 0;
221: linefeeds = 0;
222: }
223: if (linefeeds == 2) {
224: linefeeds = 0;
225: preformatted = false;
226: }
227: if (spaces == 3 && (!preformatted)) {
228: converted = converted.substring(0,
229: converted.length() - 3)
230: + "\t";
231: spaces = 0;
232: }
233: }
234: return converted;
235: }
236:
237: /**
238: *
239: */
240: public static String getDirectoryFromPath(String path) {
241: logger.debug("getDirectoryFromPath: " + path);
242: return path.substring(0, 1 + path.lastIndexOf('/'));
243: }
244:
245: /**
246: *
247: */
248: public static String sep() {
249: return System.getProperty("file.separator");
250: }
251:
252: /**
253: * Extracts the virtual wiki from the URL path. Defaults to the default
254: * wiki if there's trouble.
255: *
256: */
257: public static String extractVirtualWiki(HttpServletRequest request)
258: throws Exception {
259: String path = request.getRequestURL().toString();
260: return extractVirtualWiki(path);
261: }
262:
263: /**
264: *
265: */
266: public static String extractVirtualWiki(String path)
267: throws Exception {
268: logger.debug("path: " + path);
269: StringTokenizer tokenizer = new StringTokenizer(path, "/");
270: // if there's no tokens, use default
271: if (tokenizer.countTokens() == 0)
272: return WikiBase.DEFAULT_VWIKI;
273: String[] tokens = new String[tokenizer.countTokens()];
274: int i = 0;
275: int jspPosition = -1;
276: while (tokenizer.hasMoreTokens()) {
277: tokens[i] = tokenizer.nextToken();
278: logger.debug("tokens[" + i + "]: " + tokens[i]);
279: if (tokens[i].equalsIgnoreCase("jsp"))
280: jspPosition = i;
281: i++;
282: }
283: logger.debug("jspPosition: " + jspPosition);
284: // We didn't find "jsp" in the path
285: if (jspPosition == -1) {
286: // servlet name only (application is root level context)
287: if (i == 1) {
288: return WikiBase.DEFAULT_VWIKI;
289: } else if (isAVirtualWiki(tokens[i - 2])) {
290: return tokens[i - 2];
291: } else {
292: String errorMessage = "The virtual wiki that you have chosen "
293: + "does not exist. Verify the web address that you entered. "
294: + "If the problem continues, contact your wiki administrator";
295: throw new WikiException(errorMessage);
296: }
297: } else if (jspPosition == 0 || i == 1) {
298: // handle the case where we have a "jsp" token in the URL
299: // jsp is first in path (last element is always the servlet/jsp)
300: // if we have only the servlet/jsp name assume default virtual wiki
301: return WikiBase.DEFAULT_VWIKI;
302: } else if (isAVirtualWiki(tokens[jspPosition - 1])) {
303: return tokens[jspPosition - 1];
304: } else {
305: // if we come up with anything not in the vwiki list, we use the default
306: return WikiBase.DEFAULT_VWIKI;
307: }
308: }
309:
310: /**
311: * checks the string parameter against there virtual wiki list
312: */
313: public static boolean isAVirtualWiki(String virtualWiki) {
314: try {
315: WikiBase wikibase = WikiBase.getInstance();
316: Iterator wikilist = wikibase.getVirtualWikiList()
317: .iterator();
318: while (wikilist.hasNext()) {
319: if (virtualWiki.equals((String) wikilist.next()))
320: return true;
321: }
322: } catch (Exception e) {
323: e.printStackTrace();
324: }
325: return false;
326: }
327:
328: /**
329: *
330: */
331: public static String getUserFromRequest(HttpServletRequest request) {
332: if (request.getRemoteUser() != null) {
333: return request.getRemoteUser();
334: }
335: Cookie[] cookies = request.getCookies();
336: if (cookies == null)
337: return null;
338: if (cookies.length > 0) {
339: for (int i = 0; i < cookies.length; i++) {
340: if (cookies[i].getName().equals("username")) {
341: return cookies[i].getValue();
342: }
343: }
344: }
345: return null;
346: }
347:
348: /**
349: *
350: */
351: public static boolean checkValidTopicName(String name) {
352: if (name.indexOf(':') >= 0 || name.indexOf('/') >= 0
353: || name.indexOf('.') >= 0 || name.indexOf("%3a") >= 0
354: || name.indexOf("%2f") >= 0 || name.indexOf("%2e") >= 0) {
355: return false;
356: }
357: return true;
358: }
359:
360: /**
361: * Return a String from the ApplicationResources. The ApplicationResources contains all text,
362: * links, etc. which may be language specific and is located in the classes folder.
363: * @param key The key to get.
364: * @param locale The locale to use.
365: * @return String containing the requested text.
366: */
367: public static String resource(String key, Locale locale) {
368: ResourceBundle messages = ResourceBundle.getBundle(
369: "ApplicationResources", locale);
370: return messages.getString(key);
371: }
372:
373: /**
374: *
375: */
376: public static String convertLineFeeds(String text) {
377: StringBuffer buffer = new StringBuffer();
378: boolean rSeen = false;
379: for (int i = 0; i < text.length(); i++) {
380: char c = text.charAt(i);
381: if (c == '\n') {
382: buffer.append("\r\n");
383: } else if (c == '\r') {
384: //do nothing
385: } else {
386: buffer.append(c);
387: }
388: }
389: return buffer.toString();
390: }
391:
392: /**
393: * Read a file from the file system
394: * @param file The file to read
395: * @return a Stringbuffer with the content of this file or an empty StringBuffer, if an error has occured
396: */
397: public static StringBuffer readFile(File file) {
398: char[] buf = new char[1024];
399: StringBuffer content = new StringBuffer((int) file.length());
400: try {
401: Reader in = new BufferedReader(new InputStreamReader(
402: new FileInputStream(file), Environment
403: .getInstance().getFileEncoding()));
404: int numread = 0;
405: while ((numread = in.read(buf)) != -1) {
406: content.append(buf, 0, numread);
407: }
408: in.close();
409: } catch (IOException e1) {
410: e1.printStackTrace();
411: }
412: return content;
413: }
414:
415: /**
416: *
417: */
418: public static final void copyInputStream(InputStream in,
419: OutputStream out) throws IOException {
420: byte[] buffer = new byte[1024];
421: int len;
422: while ((len = in.read(buffer)) >= 0) {
423: out.write(buffer, 0, len);
424: }
425: in.close();
426: out.close();
427: }
428:
429: /**
430: *
431: */
432: public static void unzip(File zipFileToOpen, File outputDirectory) {
433: Enumeration entries;
434: ZipFile zipFile;
435: try {
436: zipFile = new ZipFile(zipFileToOpen);
437: entries = zipFile.entries();
438: while (entries.hasMoreElements()) {
439: ZipEntry entry = (ZipEntry) entries.nextElement();
440: if (entry.isDirectory()) {
441: logger.debug("Extracting directory: "
442: + entry.getName());
443: // This is not robust, just for demonstration purposes.
444: File file = new File(outputDirectory, entry
445: .getName());
446: file.mkdir();
447: }
448: }
449: entries = zipFile.entries();
450: while (entries.hasMoreElements()) {
451: ZipEntry entry = (ZipEntry) entries.nextElement();
452: if (!entry.isDirectory()) {
453: logger.debug("Extracting file: " + entry.getName());
454: File outputFile = new File(outputDirectory, entry
455: .getName());
456: copyInputStream(zipFile.getInputStream(entry),
457: new BufferedOutputStream(
458: new FileOutputStream(outputFile)));
459: }
460: }
461: zipFile.close();
462: } catch (IOException ioe) {
463: logger.error("Unzipping error: " + ioe);
464: ioe.printStackTrace();
465: return;
466: }
467: }
468:
469: /**
470: * Use standard factory to create DocumentBuilder and parse a file
471: */
472: public static Document parseDocumentFromFile(String fileName)
473: throws Exception {
474: File file = new File(fileName);
475: DocumentBuilderFactory factory = DocumentBuilderFactory
476: .newInstance();
477: DocumentBuilder builder = factory.newDocumentBuilder();
478: return builder.parse(file);
479: }
480:
481: /**
482: *
483: */
484: public static boolean isAdmin(HttpServletRequest request) {
485: HttpSession session = request.getSession();
486: String value = (String) session.getAttribute("admin");
487: return "true".equals(value);
488: }
489:
490: /**
491: * Replaces occurences of the find string with the replace string in the given text
492: * @param text
493: * @param find
494: * @param replace
495: * @return the altered string
496: */
497: public static String replaceString(String text, String find,
498: String replace) {
499: int findLength = find.length();
500: StringBuffer buffer = new StringBuffer();
501: int i;
502: for (i = 0; i < text.length() - find.length() + 1; i++) {
503: String substring = text.substring(i, i + findLength);
504: if (substring.equals(find)) {
505: buffer.append(replace);
506: i += find.length() - 1;
507: } else {
508: buffer.append(text.charAt(i));
509: }
510: }
511: buffer.append(text.substring(text.length()
512: - (text.length() - i)));
513: return buffer.toString();
514: }
515:
516: /**
517: * Returns any trailing . , ; : characters on the given string
518: * @param text
519: * @return empty string if none are found
520: */
521: public static String extractTrailingPunctuation(String text) {
522: StringBuffer buffer = new StringBuffer();
523: for (int i = text.length() - 1; i >= 0; i--) {
524: char c = text.charAt(i);
525: if (c == '.' || c == ';' || c == ',' || c == ':') {
526: buffer.append(c);
527: } else {
528: break;
529: }
530: }
531: if (buffer.length() == 0)
532: return "";
533: buffer = buffer.reverse();
534: return buffer.toString();
535: }
536:
537: /**
538: * Converts arbitrary string into string usable as file name.
539: */
540: public static String encodeSafeFileName(String name) {
541: // REMINDER: This code is duplicated in SearchApplet for the HTMLExport.
542: StringTokenizer st = new StringTokenizer(name, "%"
543: + File.separator, true);
544: StringBuffer sb = new StringBuffer(name.length());
545: while (st.hasMoreTokens()) {
546: String token = st.nextToken();
547: if (File.separator.equals(token) || "%".equals(token)) {
548: sb.append(token);
549: } else {
550: sb.append(JSPUtils.encodeURL(token, "utf-8"));
551: }
552: }
553: return sb.toString();
554: }
555:
556: /**
557: *
558: */
559: public static String encodeSafeExportFileName(String name) {
560: // REMINDER: This code is duplicated in SearchApplet for the HTMLExport.
561: StringBuffer sb = new StringBuffer(encodeSafeFileName(name));
562: for (int i = 0; i < sb.length(); i++) {
563: if (sb.charAt(i) == '%') {
564: sb.setCharAt(i, '-');
565: }
566: }
567: return sb.toString();
568: }
569:
570: /**
571: * Converts back file name encoded by encodeSafeFileName().
572: */
573: public static String decodeSafeFileName(String name) {
574: return JSPUtils.decodeURL(name, "utf-8");
575: }
576:
577: /**
578: * Converts CamelCase to seperate words.
579: * @author cmeans
580: *
581: */
582: public static String separateWords(String text) {
583: // Do not try to separateWords if there are spaces in the text.
584: if (text.indexOf(" ") != -1)
585: return text;
586: // Allocate enough space for the new string, plus
587: // magic number (5) for a guess at the likely max
588: // number of words.
589: StringBuffer sb = new StringBuffer(text.length() + 5);
590: int offset = 0; // points to the start of each word.
591: // Loop through the CamelCase text, at each capital letter
592: // concatenate the previous word text to the result
593: // and append a space.
594: // The first character is assumed to be a "capital".
595: for (int i = 1; i < text.length(); i++) {
596: if ((text.charAt(i) >= 'A') && (text.charAt(i) <= 'Z')) {
597: // Append the current word and a trailing space.
598: sb.append(text.substring(offset, i)).append(" ");
599: // Start of the next word.
600: offset = i;
601: }
602: }
603: // Append the last word.
604: sb.append(text.substring(offset));
605: return sb.toString();
606: }
607:
608: /**
609: * Parse DOM document from XML in input stream
610: * @param xmlIn
611: * @return
612: * @throws IOException
613: * @throws SAXException
614: * @throws ParserConfigurationException
615: */
616: public static Document parseDocumentFromInputStream(
617: InputStream xmlIn) throws IOException, SAXException,
618: ParserConfigurationException {
619: DocumentBuilderFactory factory = DocumentBuilderFactory
620: .newInstance();
621: DocumentBuilder builder = factory.newDocumentBuilder();
622: return builder.parse(xmlIn);
623: }
624: }
|